# HG changeset patch # User vromero # Date 1379181887 -3600 # Node ID 8b91e8eb2d20caec0e4ba33dc8519587c859c6ef # Parent d6d6e623f0b409acfe74fd0ea4669230a1ad4395 7047734: javac, the LVT is not generated correctly in several scenarios Reviewed-by: jjg, mcimadamore diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/code/Lint.java --- a/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java Sat Sep 14 19:04:47 2013 +0100 @@ -33,9 +33,6 @@ import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Pair; -import static com.sun.tools.javac.code.Flags.*; - - /** * A class for handling -Xlint suboptions and @SuppresssWarnings. * @@ -81,7 +78,6 @@ return l; } - private final AugmentVisitor augmentor; private final EnumSet values; @@ -90,7 +86,6 @@ private static final Map map = new java.util.concurrent.ConcurrentHashMap(20); - protected Lint(Context context) { // initialize values according to the lint options Options options = Options.instance(context); diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Sat Sep 14 19:04:47 2013 +0100 @@ -207,7 +207,7 @@ public void analyzeTree(Env env, TreeMaker make) { new AliveAnalyzer().analyzeTree(env, make); - new AssignAnalyzer().analyzeTree(env, make); + new AssignAnalyzer(log, syms, lint, names).analyzeTree(env); new FlowAnalyzer().analyzeTree(env, make); new CaptureAnalyzer().analyzeTree(env, make); } @@ -239,7 +239,7 @@ //related errors, which will allow for more errors to be detected Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); try { - new AssignAnalyzer().analyzeTree(env, that, make); + new AssignAnalyzer(log, syms, lint, names).analyzeTree(env); LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); flowAnalyzer.analyzeTree(env, that, make); return flowAnalyzer.inferredThrownTypes; @@ -292,15 +292,6 @@ } /** - * Utility method to reset several Bits instances. - */ - private void resetBits(Bits... bits) { - for (Bits b : bits) { - b.reset(); - } - } - - /** * Base visitor class for all visitors implementing dataflow analysis logic. * This class define the shared logic for handling jumps (break/continue statements). */ @@ -347,17 +338,17 @@ this.tree = tree; } - void resolveJump() { + void resolveJump(JCTree tree) { //do nothing } } - abstract void markDead(); + abstract void markDead(JCTree tree); /** Record an outward transfer of control. */ void recordExit(JCTree tree, P pe) { pendingExits.append(pe); - markDead(); + markDead(tree); } /** Resolve all jumps of this statement. */ @@ -371,7 +362,7 @@ P exit = exits.head; if (exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree) { - exit.resolveJump(); + exit.resolveJump(tree); resolved = true; } else { pendingExits.append(exit); @@ -380,12 +371,12 @@ return resolved; } - /** Resolve all breaks of this statement. */ + /** Resolve all continues of this statement. */ boolean resolveContinues(JCTree tree) { return resolveJump(tree, new ListBuffer

(), JumpKind.CONTINUE); } - /** Resolve all continues of this statement. */ + /** Resolve all breaks of this statement. */ boolean resolveBreaks(JCTree tree, ListBuffer

oldPendingExits) { return resolveJump(tree, oldPendingExits, JumpKind.BREAK); } @@ -414,7 +405,7 @@ private boolean alive; @Override - void markDead() { + void markDead(JCTree tree) { alive = false; } @@ -696,7 +687,7 @@ public void visitThrow(JCThrow tree) { scan(tree.expr); - markDead(); + markDead(tree); } public void visitApply(JCMethodInvocation tree) { @@ -797,7 +788,7 @@ } @Override - void markDead() { + void markDead(JCTree tree) { //do nothing } @@ -1222,7 +1213,7 @@ else { markThrown(tree, tree.expr.type); } - markDead(); + markDead(tree); } public void visitApply(JCMethodInvocation tree) { @@ -1372,11 +1363,13 @@ * depends on the results of the liveliness analyzer. This pass is also used to mark * effectively-final local variables/parameters. */ - class AssignAnalyzer extends BaseAnalyzer { + + public abstract static class AbstractAssignAnalyzer

+ extends BaseAnalyzer

{ /** The set of definitely assigned variables. */ - final Bits inits; + protected final Bits inits; /** The set of definitely unassigned variables. */ @@ -1402,7 +1395,7 @@ /** A mapping from addresses to variable symbols. */ - JCVariableDecl[] vardecls; + protected JCVariableDecl[] vardecls; /** The current class being defined. */ @@ -1414,11 +1407,11 @@ /** The next available variable sequence number. */ - int nextadr; + protected int nextadr; /** The first variable sequence number in a block that can return. */ - int returnadr; + protected int returnadr; /** The list of unreferenced automatic resources. */ @@ -1430,35 +1423,46 @@ /** The starting position of the analysed tree */ int startPos; - AssignAnalyzer() { - inits = new Bits(); + final Symtab syms; + + protected Names names; + + public static class AbstractAssignPendingExit extends BaseAnalyzer.PendingExit { + + final Bits inits; + final Bits uninits; + final Bits exit_inits = new Bits(true); + final Bits exit_uninits = new Bits(true); + + public AbstractAssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { + super(tree); + this.inits = inits; + this.uninits = uninits; + this.exit_inits.assign(inits); + this.exit_uninits.assign(uninits); + } + + @Override + public void resolveJump(JCTree tree) { + inits.andSet(exit_inits); + uninits.andSet(exit_uninits); + } + } + + public AbstractAssignAnalyzer(Bits inits, Symtab syms, Names names) { + this.inits = inits; uninits = new Bits(); uninitsTry = new Bits(); initsWhenTrue = new Bits(true); initsWhenFalse = new Bits(true); uninitsWhenTrue = new Bits(true); uninitsWhenFalse = new Bits(true); - } - - class AssignPendingExit extends BaseAnalyzer.PendingExit { - - final Bits exit_inits = new Bits(true); - final Bits exit_uninits = new Bits(true); - - AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { - super(tree); - this.exit_inits.assign(inits); - this.exit_uninits.assign(uninits); - } - - void resolveJump() { - inits.andSet(exit_inits); - uninits.andSet(exit_uninits); - } + this.syms = syms; + this.names = names; } @Override - void markDead() { + protected void markDead(JCTree tree) { inits.inclRange(returnadr, nextadr); uninits.inclRange(returnadr, nextadr); } @@ -1468,7 +1472,7 @@ /** Do we need to track init/uninit state of this symbol? * I.e. is symbol either a local or a blank final variable? */ - boolean trackable(VarSymbol sym) { + protected boolean trackable(VarSymbol sym) { return sym.pos >= startPos && ((sym.owner.kind == MTH || @@ -1488,44 +1492,35 @@ } sym.adr = nextadr; vardecls[nextadr] = varDecl; - inits.excl(nextadr); + exclVarFromInits(varDecl, nextadr); uninits.incl(nextadr); nextadr++; } + protected void exclVarFromInits(JCTree tree, int adr) { + inits.excl(adr); + } + + protected void assignToInits(JCTree tree, Bits bits) { + inits.assign(bits); + } + + protected void andSetInits(JCTree tree, Bits bits) { + inits.andSet(bits); + } + + protected void orSetInits(JCTree tree, Bits bits) { + inits.orSet(bits); + } + /** Record an initialization of a trackable variable. */ void letInit(DiagnosticPosition pos, VarSymbol sym) { if (sym.adr >= firstadr && trackable(sym)) { - if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { - if (!uninits.isMember(sym.adr)) { - //assignment targeting an effectively final variable - //makes the variable lose its status of effectively final - //if the variable is _not_ definitively unassigned - sym.flags_field &= ~EFFECTIVELY_FINAL; - } else { - uninit(sym); - } - } - else if ((sym.flags() & FINAL) != 0) { - if ((sym.flags() & PARAMETER) != 0) { - if ((sym.flags() & UNION) != 0) { //multi-catch parameter - log.error(pos, "multicatch.parameter.may.not.be.assigned", - sym); - } - else { - log.error(pos, "final.parameter.may.not.be.assigned", - sym); - } - } else if (!uninits.isMember(sym.adr)) { - log.error(pos, flowKind.errKey, sym); - } else { - uninit(sym); - } + if (uninits.isMember(sym.adr)) { + uninit(sym); } inits.incl(sym.adr); - } else if ((sym.flags() & FINAL) != 0) { - log.error(pos, "var.might.already.be.assigned", sym); } } //where @@ -1559,12 +1554,14 @@ void checkInit(DiagnosticPosition pos, VarSymbol sym) { checkInit(pos, sym, "var.might.not.have.been.initialized"); } - void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) { - if ((sym.adr >= firstadr || sym.owner.kind != TYP) && - trackable(sym) && - !inits.isMember(sym.adr)) { - log.error(pos, errkey, sym); - inits.incl(sym.adr); + + void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) {} + + /** Utility method to reset several Bits instances. + */ + private void resetBits(Bits... bits) { + for (Bits b : bits) { + b.reset(); } } @@ -1582,7 +1579,7 @@ /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. */ - void merge() { + protected void merge(JCTree tree) { inits.assign(initsWhenFalse.andSet(initsWhenTrue)); uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); } @@ -1597,7 +1594,9 @@ void scanExpr(JCTree tree) { if (tree != null) { scan(tree); - if (inits.isReset()) merge(); + if (inits.isReset()) { + merge(tree); + } } } @@ -1614,7 +1613,7 @@ */ void scanCond(JCTree tree) { if (tree.type.isFalse()) { - if (inits.isReset()) merge(); + if (inits.isReset()) merge(tree); initsWhenTrue.assign(inits); initsWhenTrue.inclRange(firstadr, nextadr); uninitsWhenTrue.assign(uninits); @@ -1622,7 +1621,7 @@ initsWhenFalse.assign(inits); uninitsWhenFalse.assign(uninits); } else if (tree.type.isTrue()) { - if (inits.isReset()) merge(); + if (inits.isReset()) merge(tree); initsWhenFalse.assign(inits); initsWhenFalse.inclRange(firstadr, nextadr); uninitsWhenFalse.assign(uninits); @@ -1641,22 +1640,22 @@ /* ------------ Visitor methods for various sorts of trees -------------*/ + @Override public void visitClassDef(JCClassDecl tree) { - if (tree.sym == null) return; + if (tree.sym == null) { + return; + } JCClassDecl classDefPrev = classDef; int firstadrPrev = firstadr; int nextadrPrev = nextadr; - ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; + ListBuffer

pendingExitsPrev = pendingExits; - pendingExits = new ListBuffer(); + pendingExits = new ListBuffer

(); if (tree.name != names.empty) { firstadr = nextadr; } classDef = tree; - lint = lint.augment(tree.sym); - try { // define all the static fields for (List l = tree.defs; l.nonEmpty(); l = l.tail) { @@ -1664,8 +1663,9 @@ JCVariableDecl def = (JCVariableDecl)l.head; if ((def.mods.flags & STATIC) != 0) { VarSymbol sym = def.sym; - if (trackable(sym)) + if (trackable(sym)) { newVar(def); + } } } } @@ -1684,8 +1684,9 @@ JCVariableDecl def = (JCVariableDecl)l.head; if ((def.mods.flags & STATIC) == 0) { VarSymbol sym = def.sym; - if (trackable(sym)) + if (trackable(sym)) { newVar(def); + } } } } @@ -1709,21 +1710,25 @@ nextadr = nextadrPrev; firstadr = firstadrPrev; classDef = classDefPrev; - lint = lintPrev; } } + @Override public void visitMethodDef(JCMethodDecl tree) { - if (tree.body == null) return; + if (tree.body == null) { + return; + } + /* MemberEnter can generate synthetic methods, ignore them + */ + if ((tree.sym.flags() & SYNTHETIC) != 0) { + return; + } final Bits initsPrev = new Bits(inits); final Bits uninitsPrev = new Bits(uninits); int nextadrPrev = nextadr; int firstadrPrev = firstadr; int returnadrPrev = returnadr; - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); @@ -1731,13 +1736,17 @@ boolean isInitialConstructor = TreeInfo.isInitialConstructor(tree); - if (!isInitialConstructor) + if (!isInitialConstructor) { firstadr = nextadr; + } for (List l = tree.params; l.nonEmpty(); l = l.tail) { JCVariableDecl def = l.head; scan(def); - inits.incl(def.sym.adr); - uninits.excl(def.sym.adr); + Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); + /* If we are executing the code from Gen, then there can be + * synthetic or mandated variables, ignore them. + */ + initParam(def); } // else we are in an instance initializer block; // leave caught unchanged. @@ -1761,39 +1770,42 @@ } } } - List exits = pendingExits.toList(); - pendingExits = new ListBuffer(); + List

exits = pendingExits.toList(); + pendingExits = new ListBuffer<>(); while (exits.nonEmpty()) { - AssignPendingExit exit = exits.head; + P exit = exits.head; exits = exits.tail; Assert.check(exit.tree.hasTag(RETURN), exit.tree); if (isInitialConstructor) { - inits.assign(exit.exit_inits); - for (int i = firstadr; i < nextadr; i++) + assignToInits(exit.tree, exit.exit_inits); + for (int i = firstadr; i < nextadr; i++) { checkInit(exit.tree.pos(), vardecls[i].sym); + } } } } finally { - inits.assign(initsPrev); + assignToInits(tree, initsPrev); uninits.assign(uninitsPrev); nextadr = nextadrPrev; firstadr = firstadrPrev; returnadr = returnadrPrev; - lint = lintPrev; } } + protected void initParam(JCVariableDecl def) { + inits.incl(def.sym.adr); + uninits.excl(def.sym.adr); + } + public void visitVarDef(JCVariableDecl tree) { boolean track = trackable(tree.sym); - if (track && tree.sym.owner.kind == MTH) newVar(tree); + if (track && tree.sym.owner.kind == MTH) { + newVar(tree); + } if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scanExpr(tree.init); - if (track) letInit(tree.pos(), tree.sym); - } finally { - lint = lintPrev; + scanExpr(tree.init); + if (track) { + letInit(tree.pos(), tree.sym); } } } @@ -1804,14 +1816,18 @@ nextadr = nextadrPrev; } + int getLogNumberOfErrors() { + return 0; + } + public void visitDoLoop(JCDoWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer

prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; final Bits initsSkip = new Bits(true); final Bits uninitsSkip = new Bits(true); - pendingExits = new ListBuffer(); - int prevErrors = log.nerrors; + pendingExits = new ListBuffer

(); + int prevErrors = getLogNumberOfErrors(); do { final Bits uninitsEntry = new Bits(uninits); uninitsEntry.excludeFrom(nextadr); @@ -1822,28 +1838,28 @@ initsSkip.assign(initsWhenFalse); uninitsSkip.assign(uninitsWhenFalse); } - if (log.nerrors != prevErrors || + if (getLogNumberOfErrors() != prevErrors || flowKind.isFinal() || new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) break; - inits.assign(initsWhenTrue); + assignToInits(tree.cond, initsWhenTrue); uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); flowKind = FlowKind.SPECULATIVE_LOOP; } while (true); flowKind = prevFlowKind; - inits.assign(initsSkip); + assignToInits(tree, initsSkip); uninits.assign(uninitsSkip); resolveBreaks(tree, prevPendingExits); } public void visitWhileLoop(JCWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer

prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; final Bits initsSkip = new Bits(true); final Bits uninitsSkip = new Bits(true); - pendingExits = new ListBuffer(); - int prevErrors = log.nerrors; + pendingExits = new ListBuffer<>(); + int prevErrors = getLogNumberOfErrors(); final Bits uninitsEntry = new Bits(uninits); uninitsEntry.excludeFrom(nextadr); do { @@ -1852,35 +1868,36 @@ initsSkip.assign(initsWhenFalse) ; uninitsSkip.assign(uninitsWhenFalse); } - inits.assign(initsWhenTrue); + assignToInits(tree, initsWhenTrue); uninits.assign(uninitsWhenTrue); scan(tree.body); resolveContinues(tree); - if (log.nerrors != prevErrors || + if (getLogNumberOfErrors() != prevErrors || flowKind.isFinal() || - new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) + new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { break; + } uninits.assign(uninitsEntry.andSet(uninits)); flowKind = FlowKind.SPECULATIVE_LOOP; } while (true); flowKind = prevFlowKind; //a variable is DA/DU after the while statement, if it's DA/DU assuming the //branch is not taken AND if it's DA/DU before any break statement - inits.assign(initsSkip); + assignToInits(tree.body, initsSkip); uninits.assign(uninitsSkip); resolveBreaks(tree, prevPendingExits); } public void visitForLoop(JCForLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer

prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; int nextadrPrev = nextadr; scan(tree.init); final Bits initsSkip = new Bits(true); final Bits uninitsSkip = new Bits(true); - pendingExits = new ListBuffer(); - int prevErrors = log.nerrors; + pendingExits = new ListBuffer

(); + int prevErrors = getLogNumberOfErrors(); do { final Bits uninitsEntry = new Bits(uninits); uninitsEntry.excludeFrom(nextadr); @@ -1890,7 +1907,7 @@ initsSkip.assign(initsWhenFalse); uninitsSkip.assign(uninitsWhenFalse); } - inits.assign(initsWhenTrue); + assignToInits(tree.body, initsWhenTrue); uninits.assign(uninitsWhenTrue); } else if (!flowKind.isFinal()) { initsSkip.assign(inits); @@ -1901,7 +1918,7 @@ scan(tree.body); resolveContinues(tree); scan(tree.step); - if (log.nerrors != prevErrors || + if (getLogNumberOfErrors() != prevErrors || flowKind.isFinal() || new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) break; @@ -1911,7 +1928,7 @@ flowKind = prevFlowKind; //a variable is DA/DU after a for loop, if it's DA/DU assuming the //branch is not taken AND if it's DA/DU before any break statement - inits.assign(initsSkip); + assignToInits(tree.body, initsSkip); uninits.assign(uninitsSkip); resolveBreaks(tree, prevPendingExits); nextadr = nextadrPrev; @@ -1920,7 +1937,7 @@ public void visitForeachLoop(JCEnhancedForLoop tree) { visitVarDef(tree.var); - ListBuffer prevPendingExits = pendingExits; + ListBuffer

prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; int nextadrPrev = nextadr; @@ -1929,14 +1946,14 @@ final Bits uninitsStart = new Bits(uninits); letInit(tree.pos(), tree.var.sym); - pendingExits = new ListBuffer(); - int prevErrors = log.nerrors; + pendingExits = new ListBuffer

(); + int prevErrors = getLogNumberOfErrors(); do { final Bits uninitsEntry = new Bits(uninits); uninitsEntry.excludeFrom(nextadr); scan(tree.body); resolveContinues(tree); - if (log.nerrors != prevErrors || + if (getLogNumberOfErrors() != prevErrors || flowKind.isFinal() || new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) break; @@ -1944,41 +1961,50 @@ flowKind = FlowKind.SPECULATIVE_LOOP; } while (true); flowKind = prevFlowKind; - inits.assign(initsStart); + assignToInits(tree.body, initsStart); uninits.assign(uninitsStart.andSet(uninits)); resolveBreaks(tree, prevPendingExits); nextadr = nextadrPrev; } public void visitLabelled(JCLabeledStatement tree) { - ListBuffer prevPendingExits = pendingExits; - pendingExits = new ListBuffer(); + ListBuffer

prevPendingExits = pendingExits; + pendingExits = new ListBuffer

(); scan(tree.body); resolveBreaks(tree, prevPendingExits); } public void visitSwitch(JCSwitch tree) { - ListBuffer prevPendingExits = pendingExits; - pendingExits = new ListBuffer(); + ListBuffer

prevPendingExits = pendingExits; + pendingExits = new ListBuffer<>(); int nextadrPrev = nextadr; scanExpr(tree.selector); final Bits initsSwitch = new Bits(inits); final Bits uninitsSwitch = new Bits(uninits); boolean hasDefault = false; for (List l = tree.cases; l.nonEmpty(); l = l.tail) { - inits.assign(initsSwitch); + assignToInits(l.head, initsSwitch); uninits.assign(uninits.andSet(uninitsSwitch)); JCCase c = l.head; - if (c.pat == null) + if (c.pat == null) { hasDefault = true; - else + } else { scanExpr(c.pat); + } + if (hasDefault) { + assignToInits(null, initsSwitch); + uninits.assign(uninits.andSet(uninitsSwitch)); + } scan(c.stats); addVars(c.stats, initsSwitch, uninitsSwitch); + if (!hasDefault) { + assignToInits(l.head.stats.last(), initsSwitch); + uninits.assign(uninits.andSet(uninitsSwitch)); + } // Warn about fall-through if lint switch fallthrough enabled. } if (!hasDefault) { - inits.andSet(initsSwitch); + andSetInits(null, initsSwitch); } resolveBreaks(tree, prevPendingExits); nextadr = nextadrPrev; @@ -1997,11 +2023,17 @@ } } + boolean isEnabled(Lint.LintCategory lc) { + return false; + } + + void reportWarning(Lint.LintCategory lc, DiagnosticPosition pos, String key, Object ... args) {} + public void visitTry(JCTry tree) { ListBuffer resourceVarDecls = ListBuffer.lb(); final Bits uninitsTryPrev = new Bits(uninitsTry); - ListBuffer prevPendingExits = pendingExits; - pendingExits = new ListBuffer(); + ListBuffer

prevPendingExits = pendingExits; + pendingExits = new ListBuffer<>(); final Bits initsTry = new Bits(inits); uninitsTry.assign(uninits); for (JCTree resource : tree.resources) { @@ -2023,10 +2055,10 @@ int nextadrCatch = nextadr; if (!resourceVarDecls.isEmpty() && - lint.isEnabled(Lint.LintCategory.TRY)) { + isEnabled(Lint.LintCategory.TRY)) { for (JCVariableDecl resVar : resourceVarDecls) { if (unrefdResources.includes(resVar.sym)) { - log.warning(Lint.LintCategory.TRY, resVar.pos(), + reportWarning(Lint.LintCategory.TRY, resVar.pos(), "try.resource.not.referenced", resVar.sym); unrefdResources.remove(resVar.sym); } @@ -2042,20 +2074,22 @@ for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { JCVariableDecl param = l.head.param; - inits.assign(initsCatchPrev); + assignToInits(tree.body, initsCatchPrev); uninits.assign(uninitsCatchPrev); scan(param); - inits.incl(param.sym.adr); - uninits.excl(param.sym.adr); + /* If this is a TWR and we are executing the code from Gen, + * then there can be synthetic variables, ignore them. + */ + initParam(param); scan(l.head.body); initsEnd.andSet(inits); uninitsEnd.andSet(uninits); nextadr = nextadrCatch; } if (tree.finalizer != null) { - inits.assign(initsTry); + assignToInits(tree.finalizer, initsTry); uninits.assign(uninitsTry); - ListBuffer exits = pendingExits; + ListBuffer

exits = pendingExits; pendingExits = prevPendingExits; scan(tree.finalizer); if (!tree.finallyCanCompleteNormally) { @@ -2065,19 +2099,19 @@ // FIX: this doesn't preserve source order of exits in catch // versus finally! while (exits.nonEmpty()) { - AssignPendingExit exit = exits.next(); + P exit = exits.next(); if (exit.exit_inits != null) { exit.exit_inits.orSet(inits); exit.exit_uninits.andSet(uninits); } pendingExits.append(exit); } - inits.orSet(initsEnd); + orSetInits(tree, initsEnd); } } else { - inits.assign(initsEnd); + assignToInits(tree, initsEnd); uninits.assign(uninitsEnd); - ListBuffer exits = pendingExits; + ListBuffer

exits = pendingExits; pendingExits = prevPendingExits; while (exits.nonEmpty()) pendingExits.append(exits.next()); } @@ -2088,7 +2122,7 @@ scanCond(tree.cond); final Bits initsBeforeElse = new Bits(initsWhenFalse); final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); - inits.assign(initsWhenTrue); + assignToInits(tree.cond, initsWhenTrue); uninits.assign(uninitsWhenTrue); if (tree.truepart.type.hasTag(BOOLEAN) && tree.falsepart.type.hasTag(BOOLEAN)) { @@ -2101,7 +2135,7 @@ final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); - inits.assign(initsBeforeElse); + assignToInits(tree.truepart, initsBeforeElse); uninits.assign(uninitsBeforeElse); scanCond(tree.falsepart); initsWhenTrue.andSet(initsAfterThenWhenTrue); @@ -2112,10 +2146,10 @@ scanExpr(tree.truepart); final Bits initsAfterThen = new Bits(inits); final Bits uninitsAfterThen = new Bits(uninits); - inits.assign(initsBeforeElse); + assignToInits(tree.truepart, initsBeforeElse); uninits.assign(uninitsBeforeElse); scanExpr(tree.falsepart); - inits.andSet(initsAfterThen); + andSetInits(tree.falsepart, initsAfterThen); uninits.andSet(uninitsAfterThen); } } @@ -2124,39 +2158,46 @@ scanCond(tree.cond); final Bits initsBeforeElse = new Bits(initsWhenFalse); final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); - inits.assign(initsWhenTrue); + assignToInits(tree.cond, initsWhenTrue); uninits.assign(uninitsWhenTrue); scan(tree.thenpart); if (tree.elsepart != null) { final Bits initsAfterThen = new Bits(inits); final Bits uninitsAfterThen = new Bits(uninits); - inits.assign(initsBeforeElse); + assignToInits(tree.thenpart, initsBeforeElse); uninits.assign(uninitsBeforeElse); scan(tree.elsepart); - inits.andSet(initsAfterThen); + andSetInits(tree.elsepart, initsAfterThen); uninits.andSet(uninitsAfterThen); } else { - inits.andSet(initsBeforeElse); + andSetInits(tree.thenpart, initsBeforeElse); uninits.andSet(uninitsBeforeElse); } } + protected P createNewPendingExit(JCTree tree, Bits inits, Bits uninits) { + return null; + } + + @Override public void visitBreak(JCBreak tree) { - recordExit(tree, new AssignPendingExit(tree, inits, uninits)); + recordExit(tree, createNewPendingExit(tree, inits, uninits)); } + @Override public void visitContinue(JCContinue tree) { - recordExit(tree, new AssignPendingExit(tree, inits, uninits)); + recordExit(tree, createNewPendingExit(tree, inits, uninits)); } + @Override public void visitReturn(JCReturn tree) { scanExpr(tree.expr); - recordExit(tree, new AssignPendingExit(tree, inits, uninits)); + recordExit(tree, createNewPendingExit(tree, inits, uninits)); } public void visitThrow(JCThrow tree) { scanExpr(tree.expr); - markDead(); + markDead(tree.expr); } public void visitApply(JCMethodInvocation tree) { @@ -2175,10 +2216,10 @@ final Bits prevUninits = new Bits(uninits); final Bits prevInits = new Bits(inits); int returnadrPrev = returnadr; - ListBuffer prevPending = pendingExits; + ListBuffer

prevPending = pendingExits; try { returnadr = nextadr; - pendingExits = new ListBuffer(); + pendingExits = new ListBuffer

(); for (List l = tree.params; l.nonEmpty(); l = l.tail) { JCVariableDecl def = l.head; scan(def); @@ -2194,7 +2235,7 @@ finally { returnadr = returnadrPrev; uninits.assign(prevUninits); - inits.assign(prevInits); + assignToInits(tree, prevInits); pendingExits = prevPending; } } @@ -2210,11 +2251,11 @@ scanCond(tree.cond); uninitsExit.andSet(uninitsWhenTrue); if (tree.detail != null) { - inits.assign(initsWhenFalse); + assignToInits(tree, initsWhenFalse); uninits.assign(uninitsWhenFalse); scanExpr(tree.detail); } - inits.assign(initsExit); + assignToInits(tree, initsExit); uninits.assign(uninitsExit); } @@ -2260,7 +2301,7 @@ scanCond(tree.lhs); final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); - inits.assign(initsWhenTrue); + assignToInits(tree.lhs, initsWhenTrue); uninits.assign(uninitsWhenTrue); scanCond(tree.rhs); initsWhenFalse.andSet(initsWhenFalseLeft); @@ -2270,7 +2311,7 @@ scanCond(tree.lhs); final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); - inits.assign(initsWhenFalse); + assignToInits(tree.lhs, initsWhenFalse); uninits.assign(uninitsWhenFalse); scanCond(tree.rhs); initsWhenTrue.andSet(initsWhenTrueLeft); @@ -2308,14 +2349,12 @@ /** Perform definite assignment/unassignment analysis on a tree. */ - public void analyzeTree(Env env, TreeMaker make) { - analyzeTree(env, env.tree, make); - } + public void analyzeTree(Env env) { + analyzeTree(env, env.tree); + } - public void analyzeTree(Env env, JCTree tree, TreeMaker make) { + public void analyzeTree(Env env, JCTree tree) { try { - attrEnv = env; - Flow.this.make = make; startPos = tree.pos().getStartPosition(); if (vardecls == null) @@ -2325,7 +2364,7 @@ vardecls[i] = null; firstadr = 0; nextadr = 0; - pendingExits = new ListBuffer(); + pendingExits = new ListBuffer<>(); this.classDef = null; unrefdResources = new Scope(env.enclClass.sym); scan(tree); @@ -2334,18 +2373,160 @@ startPos = -1; resetBits(inits, uninits, uninitsTry, initsWhenTrue, initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); - if (vardecls != null) for (int i=0; i { + + Log log; + Lint lint; + + public static class AssignPendingExit + extends AbstractAssignAnalyzer.AbstractAssignPendingExit { + + public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { + super(tree, inits, uninits); + } + } + + public AssignAnalyzer(Log log, Symtab syms, Lint lint, Names names) { + super(new Bits(), syms, names); + this.log = log; + this.lint = lint; + } + + @Override + protected AssignPendingExit createNewPendingExit(JCTree tree, + Bits inits, Bits uninits) { + return new AssignPendingExit(tree, inits, uninits); + } + + /** Record an initialization of a trackable variable. + */ + @Override + void letInit(DiagnosticPosition pos, VarSymbol sym) { + if (sym.adr >= firstadr && trackable(sym)) { + if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { + if (!uninits.isMember(sym.adr)) { + //assignment targeting an effectively final variable + //makes the variable lose its status of effectively final + //if the variable is _not_ definitively unassigned + sym.flags_field &= ~EFFECTIVELY_FINAL; + } else { + uninit(sym); + } + } + else if ((sym.flags() & FINAL) != 0) { + if ((sym.flags() & PARAMETER) != 0) { + if ((sym.flags() & UNION) != 0) { //multi-catch parameter + log.error(pos, "multicatch.parameter.may.not.be.assigned", sym); + } + else { + log.error(pos, "final.parameter.may.not.be.assigned", + sym); + } + } else if (!uninits.isMember(sym.adr)) { + log.error(pos, flowKind.errKey, sym); + } else { + uninit(sym); + } + } + inits.incl(sym.adr); + } else if ((sym.flags() & FINAL) != 0) { + log.error(pos, "var.might.already.be.assigned", sym); + } + } + + @Override + void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) { + if ((sym.adr >= firstadr || sym.owner.kind != TYP) && + trackable(sym) && + !inits.isMember(sym.adr)) { + log.error(pos, errkey, sym); + inits.incl(sym.adr); + } + } + + @Override + void reportWarning(Lint.LintCategory lc, DiagnosticPosition pos, + String key, Object ... args) { + log.warning(lc, pos, key, args); + } + + @Override + int getLogNumberOfErrors() { + return log.nerrors; + } + + @Override + boolean isEnabled(Lint.LintCategory lc) { + return lint.isEnabled(lc); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + if (tree.sym == null) { + return; + } + + Lint lintPrev = lint; + lint = lint.augment(tree.sym); + try { + super.visitClassDef(tree); + } finally { + lint = lintPrev; + } + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + if (tree.body == null) { + return; + } + + /* MemberEnter can generate synthetic methods ignore them + */ + if ((tree.sym.flags() & SYNTHETIC) != 0) { + return; + } + + Lint lintPrev = lint; + lint = lint.augment(tree.sym); + try { + super.visitMethodDef(tree); + } finally { + lint = lintPrev; + } + } + + @Override + public void visitVarDef(JCVariableDecl tree) { + if (tree.init == null) { + super.visitVarDef(tree); + } else { + Lint lintPrev = lint; + lint = lint.augment(tree.sym); + try{ + super.visitVarDef(tree); + } finally { + lint = lintPrev; + } + } + } + + } + /** * This pass implements the last step of the dataflow analysis, namely * the effectively-final analysis check. This checks that every local variable @@ -2358,7 +2539,7 @@ JCTree currentTree; //local class or lambda @Override - void markDead() { + void markDead(JCTree tree) { //do nothing } diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Sat Sep 14 19:04:47 2013 +0100 @@ -1745,6 +1745,11 @@ // Just erase the type var ret = new VarSymbol(sym.flags(), name, types.erasure(sym.type), sym.owner); + + /* this information should also be kept for LVT generation at Gen + * a Symbol with pos < startPos won't be tracked. + */ + ((VarSymbol)ret).pos = ((VarSymbol)sym).pos; break; case CAPTURED_VAR: ret = new VarSymbol(SYNTHETIC | FINAL, name, types.erasure(sym.type), translatedSym) { diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Sat Sep 14 19:04:47 2013 +0100 @@ -1479,7 +1479,12 @@ * @param owner The class in which the definitions go. */ List freevarDefs(int pos, List freevars, Symbol owner) { - long flags = FINAL | SYNTHETIC; + return freevarDefs(pos, freevars, owner, 0); + } + + List freevarDefs(int pos, List freevars, Symbol owner, + long additionalFlags) { + long flags = FINAL | SYNTHETIC | additionalFlags; if (owner.kind == TYP && target.usePrivateSyntheticFields()) flags |= PRIVATE; @@ -1542,7 +1547,7 @@ (owner.isConstructor() && c.isInner() && !c.isPrivate() && !c.isStatic()); long flags = - FINAL | (isMandated ? MANDATED : SYNTHETIC); + FINAL | (isMandated ? MANDATED : SYNTHETIC) | PARAMETER; VarSymbol outerThis = makeOuterThisVarSymbol(owner, flags); owner.extraParams = owner.extraParams.prepend(outerThis); return makeOuterThisVarDecl(pos, outerThis); @@ -1626,7 +1631,8 @@ JCTree makeTwrTry(JCTry tree) { make_at(tree.pos()); twrVars = twrVars.dup(); - JCBlock twrBlock = makeTwrBlock(tree.resources, tree.body, 0); + JCBlock twrBlock = makeTwrBlock(tree.resources, tree.body, + tree.finallyCanCompleteNormally, 0); if (tree.catchers.isEmpty() && tree.finalizer == null) result = translate(twrBlock); else @@ -1635,7 +1641,8 @@ return result; } - private JCBlock makeTwrBlock(List resources, JCBlock block, int depth) { + private JCBlock makeTwrBlock(List resources, JCBlock block, + boolean finallyCanCompleteNormally, int depth) { if (resources.isEmpty()) return block; @@ -1691,17 +1698,20 @@ make.at(TreeInfo.endPos(block)); JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr); make.at(oldPos); - JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block, depth + 1), + JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block, + finallyCanCompleteNormally, depth + 1), List.of(catchClause), finallyClause); + outerTry.finallyCanCompleteNormally = finallyCanCompleteNormally; stats.add(outerTry); - return make.Block(0L, stats.toList()); + JCBlock newBlock = make.Block(0L, stats.toList()); + return newBlock; } private JCBlock makeTwrFinallyClause(Symbol primaryException, JCExpression resource) { // primaryException.addSuppressed(catchException); VarSymbol catchException = - new VarSymbol(0, make.paramName(2), + new VarSymbol(SYNTHETIC, make.paramName(2), syms.throwableType, currentMethodSym); JCStatement addSuppressionStatement = @@ -1716,6 +1726,7 @@ JCBlock catchBlock = make.Block(0L, List.of(addSuppressionStatement)); List catchClauses = List.of(make.Catch(catchExceptionDecl, catchBlock)); JCTry tryTree = make.Try(tryBlock, catchClauses, null); + tryTree.finallyCanCompleteNormally = true; // if (primaryException != null) {try...} else resourceClose; JCIf closeIfStatement = make.If(makeNonNullCheck(make.Ident(primaryException)), @@ -2016,7 +2027,7 @@ // catchParam := ClassNotFoundException e1 VarSymbol catchParam = - new VarSymbol(0, make.paramName(1), + new VarSymbol(SYNTHETIC, make.paramName(1), syms.classNotFoundExceptionType, classDollarSym); @@ -2704,7 +2715,7 @@ JCVariableDecl otdef = null; if (currentClass.hasOuterInstance()) otdef = outerThisDef(tree.pos, m); - List fvdefs = freevarDefs(tree.pos, fvs, m); + List fvdefs = freevarDefs(tree.pos, fvs, m, PARAMETER); // Recursively translate result type, parameters and thrown list. tree.restype = translate(tree.restype); @@ -3363,18 +3374,18 @@ */ private void visitArrayForeachLoop(JCEnhancedForLoop tree) { make_at(tree.expr.pos()); - VarSymbol arraycache = new VarSymbol(0, + VarSymbol arraycache = new VarSymbol(SYNTHETIC, names.fromString("arr" + target.syntheticNameChar()), tree.expr.type, currentMethodSym); JCStatement arraycachedef = make.VarDef(arraycache, tree.expr); - VarSymbol lencache = new VarSymbol(0, + VarSymbol lencache = new VarSymbol(SYNTHETIC, names.fromString("len" + target.syntheticNameChar()), syms.intType, currentMethodSym); JCStatement lencachedef = make. VarDef(lencache, make.Select(make.Ident(arraycache), syms.lengthVar)); - VarSymbol index = new VarSymbol(0, + VarSymbol index = new VarSymbol(SYNTHETIC, names.fromString("i" + target.syntheticNameChar()), syms.intType, currentMethodSym); @@ -3456,7 +3467,7 @@ names.iterator, eType, List.nil()); - VarSymbol itvar = new VarSymbol(0, names.fromString("i" + target.syntheticNameChar()), + VarSymbol itvar = new VarSymbol(SYNTHETIC, names.fromString("i" + target.syntheticNameChar()), types.erasure(types.asSuper(iterator.type.getReturnType(), syms.iteratorType.tsym)), currentMethodSym); diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Sat Sep 14 19:04:47 2013 +0100 @@ -1532,7 +1532,7 @@ * parameters from baseInit. */ initParams = List.nil(); - VarSymbol param = new VarSymbol(0, make.paramName(0), argtypes.head, init); + VarSymbol param = new VarSymbol(PARAMETER, make.paramName(0), argtypes.head, init); initParams = initParams.append(param); argTypesList = argTypesList.tail; } @@ -1541,7 +1541,7 @@ initParams = (initParams == null) ? List.nil() : initParams; List baseInitParams = baseInit.params; while (baseInitParams.nonEmpty() && argTypesList.nonEmpty()) { - VarSymbol param = new VarSymbol(baseInitParams.head.flags(), + VarSymbol param = new VarSymbol(baseInitParams.head.flags() | PARAMETER, baseInitParams.head.name, argTypesList.head, init); initParams = initParams.append(param); baseInitParams = baseInitParams.tail; diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Sat Sep 14 19:04:47 2013 +0100 @@ -310,7 +310,7 @@ Type.MethodType mType = (Type.MethodType)bridgeType; List argTypes = mType.argtypes; while (implParams.nonEmpty() && argTypes.nonEmpty()) { - VarSymbol param = new VarSymbol(implParams.head.flags() | SYNTHETIC, + VarSymbol param = new VarSymbol(implParams.head.flags() | SYNTHETIC | PARAMETER, implParams.head.name, argTypes.head, bridge); param.setAttributes(implParams.head); bridgeParams = bridgeParams.append(param); diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java --- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Sat Sep 14 19:04:47 2013 +0100 @@ -37,7 +37,6 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Attribute.RetentionPolicy; -import com.sun.tools.javac.code.Attribute.TypeCompound; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.code.Types.UniqueType; @@ -55,7 +54,6 @@ import static com.sun.tools.javac.main.Option.*; import static javax.tools.StandardLocation.CLASS_OUTPUT; - /** This class provides operations to map an internal symbol table graph * rooted in a ClassSymbol into a classfile. * @@ -1180,25 +1178,26 @@ if (code.varBufferSize > 0) { int alenIdx = writeAttr(names.LocalVariableTable); - databuf.appendChar(code.varBufferSize); - + databuf.appendChar(code.getLVTSize()); for (int i=0; i= 0 - && var.start_pc <= code.cp); - databuf.appendChar(var.start_pc); - Assert.check(var.length >= 0 - && (var.start_pc + var.length) <= code.cp); - databuf.appendChar(var.length); - VarSymbol sym = var.sym; - databuf.appendChar(pool.put(sym.name)); - Type vartype = sym.erasure(types); - if (needsLocalVariableTypeEntry(sym.type)) - nGenericVars++; - databuf.appendChar(pool.put(typeSig(vartype))); - databuf.appendChar(var.reg); + for (Code.LocalVar.Range r: var.aliveRanges) { + // write variable info + Assert.check(r.start_pc >= 0 + && r.start_pc <= code.cp); + databuf.appendChar(r.start_pc); + Assert.check(r.length >= 0 + && (r.start_pc + r.length) <= code.cp); + databuf.appendChar(r.length); + VarSymbol sym = var.sym; + databuf.appendChar(pool.put(sym.name)); + Type vartype = sym.erasure(types); + databuf.appendChar(pool.put(typeSig(vartype))); + databuf.appendChar(var.reg); + if (needsLocalVariableTypeEntry(var.sym.type)) + nGenericVars++; + } } endAttr(alenIdx); acount++; @@ -1214,13 +1213,15 @@ VarSymbol sym = var.sym; if (!needsLocalVariableTypeEntry(sym.type)) continue; - count++; - // write variable info - databuf.appendChar(var.start_pc); - databuf.appendChar(var.length); - databuf.appendChar(pool.put(sym.name)); - databuf.appendChar(pool.put(typeSig(sym.type))); - databuf.appendChar(var.reg); + for (Code.LocalVar.Range r : var.aliveRanges) { + // write variable info + databuf.appendChar(r.start_pc); + databuf.appendChar(r.length); + databuf.appendChar(pool.put(sym.name)); + databuf.appendChar(pool.put(typeSig(sym.type))); + databuf.appendChar(var.reg); + count++; + } } Assert.check(count == nGenericVars); endAttr(alenIdx); diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java --- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java Sat Sep 14 19:04:47 2013 +0100 @@ -28,6 +28,7 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Types.UniqueType; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -181,6 +182,8 @@ final MethodSymbol meth; + final LVTRanges lvtRanges; + /** Construct a code object, given the settings of the fatcode, * debugging info switches and the CharacterRangeTable. */ @@ -193,7 +196,8 @@ CRTable crt, Symtab syms, Types types, - Pool pool) { + Pool pool, + LVTRanges lvtRanges) { this.meth = meth; this.fatcode = fatcode; this.lineMap = lineMap; @@ -215,6 +219,7 @@ state = new State(); lvar = new LocalVar[20]; this.pool = pool; + this.lvtRanges = lvtRanges; } @@ -305,9 +310,19 @@ /** The current output code pointer. */ - public int curPc() { - if (pendingJumps != null) resolvePending(); - if (pendingStatPos != Position.NOPOS) markStatBegin(); + public int curCP() { + /* + * This method has side-effects because calling it can indirectly provoke + * extra code generation, like goto instructions, depending on the context + * where it's called. + * Use with care or even better avoid using it. + */ + if (pendingJumps != null) { + resolvePending(); + } + if (pendingStatPos != Position.NOPOS) { + markStatBegin(); + } fixedPc = true; return cp; } @@ -1175,7 +1190,7 @@ /** Declare an entry point; return current code pointer */ public int entryPoint() { - int pc = curPc(); + int pc = curCP(); alive = true; pendingStackMap = needStackMap; return pc; @@ -1185,7 +1200,7 @@ * return current code pointer */ public int entryPoint(State state) { - int pc = curPc(); + int pc = curCP(); alive = true; this.state = state.dup(); Assert.check(state.stacksize <= max_stack); @@ -1198,7 +1213,7 @@ * return current code pointer */ public int entryPoint(State state, Type pushed) { - int pc = curPc(); + int pc = curCP(); alive = true; this.state = state.dup(); Assert.check(state.stacksize <= max_stack); @@ -1238,7 +1253,7 @@ /** Emit a stack map entry. */ public void emitStackMap() { - int pc = curPc(); + int pc = curCP(); if (!needStackMap) return; @@ -1482,6 +1497,9 @@ chain.pc + 3 == target && target == cp && !fixedPc) { // If goto the next instruction, the jump is not needed: // compact the code. + if (varDebugInfo) { + adjustAliveRanges(cp, -3); + } cp = cp - 3; target = target - 3; if (chain.next == null) { @@ -1781,8 +1799,7 @@ sym = sym.clone(sym.owner); sym.type = newtype; LocalVar newlv = lvar[i] = new LocalVar(sym); - // should the following be initialized to cp? - newlv.start_pc = lv.start_pc; + newlv.aliveRanges = lv.aliveRanges; } } } @@ -1870,8 +1887,36 @@ static class LocalVar { final VarSymbol sym; final char reg; - char start_pc = Character.MAX_VALUE; - char length = Character.MAX_VALUE; + + class Range { + char start_pc = Character.MAX_VALUE; + char length = Character.MAX_VALUE; + + Range() {} + + Range(char start) { + this.start_pc = start; + } + + Range(char start, char length) { + this.start_pc = start; + this.length = length; + } + + boolean closed() { + return start_pc != Character.MAX_VALUE && length != Character.MAX_VALUE; + } + + @Override + public String toString() { + int currentStartPC = start_pc; + int currentLength = length; + return "startpc = " + currentStartPC + " length " + currentLength; + } + } + + java.util.List aliveRanges = new java.util.ArrayList<>(); + LocalVar(VarSymbol v) { this.sym = v; this.reg = (char)v.adr; @@ -1879,9 +1924,78 @@ public LocalVar dup() { return new LocalVar(sym); } + + Range firstRange() { + return aliveRanges.isEmpty() ? null : aliveRanges.get(0); + } + + Range lastRange() { + return aliveRanges.isEmpty() ? null : aliveRanges.get(aliveRanges.size() - 1); + } + + @Override public String toString() { - return "" + sym + " in register " + ((int)reg) + " starts at pc=" + ((int)start_pc) + " length=" + ((int)length); + if (aliveRanges == null) { + return "empty local var"; + } + StringBuilder sb = new StringBuilder().append(sym) + .append(" in register ").append((int)reg).append(" \n"); + for (Range r : aliveRanges) { + sb.append(" starts at pc=").append(Integer.toString(((int)r.start_pc))) + .append(" length=").append(Integer.toString(((int)r.length))) + .append("\n"); + } + return sb.toString(); + } + + public void openRange(char start) { + if (!hasOpenRange()) { + aliveRanges.add(new Range(start)); + } } + + public void closeRange(char end) { + if (isLastRangeInitialized()) { + Range range = lastRange(); + if (range != null) { + if (range.length == Character.MAX_VALUE) { + range.length = end; + } + } + } else { + if (!aliveRanges.isEmpty()) { + aliveRanges.remove(aliveRanges.size() - 1); + } + } + } + + public boolean hasOpenRange() { + if (aliveRanges.isEmpty()) { + return false; + } + Range range = lastRange(); + return range.length == Character.MAX_VALUE; + } + + public boolean isLastRangeInitialized() { + if (aliveRanges.isEmpty()) { + return false; + } + Range range = lastRange(); + return range.start_pc != Character.MAX_VALUE; + } + + public Range getWidestRange() { + if (aliveRanges.isEmpty()) { + return new Range(); + } else { + Range firstRange = firstRange(); + Range lastRange = lastRange(); + char length = (char)(lastRange.length + (lastRange.start_pc - firstRange.start_pc)); + return new Range(firstRange.start_pc, length); + } + } + }; /** Local variables, indexed by register. */ @@ -1892,11 +2006,60 @@ int adr = v.adr; lvar = ArrayUtils.ensureCapacity(lvar, adr+1); Assert.checkNull(lvar[adr]); - if (pendingJumps != null) resolvePending(); + if (pendingJumps != null) { + resolvePending(); + } lvar[adr] = new LocalVar(v); state.defined.excl(adr); } + + public void closeAliveRanges(JCTree tree) { + closeAliveRanges(tree, cp); + } + + public void closeAliveRanges(JCTree tree, int closingCP) { + List locals = lvtRanges.getVars(meth, tree); + for (LocalVar localVar: lvar) { + for (VarSymbol aliveLocal : locals) { + if (localVar == null) { + return; + } + if (localVar.sym == aliveLocal && localVar.lastRange() != null) { + char length = (char)(closingCP - localVar.lastRange().start_pc); + if (length > 0 && length < Character.MAX_VALUE) { + localVar.closeRange(length); + } + } + } + } + } + + void adjustAliveRanges(int oldCP, int delta) { + for (LocalVar localVar: lvar) { + if (localVar == null) { + return; + } + for (LocalVar.Range range: localVar.aliveRanges) { + if (range.closed() && range.start_pc + range.length >= oldCP) { + range.length += delta; + } + } + } + } + + /** + * Calculates the size of the LocalVariableTable. + */ + public int getLVTSize() { + int result = varBufferSize; + for (int i = 0; i < varBufferSize; i++) { + LocalVar var = varBuffer[i]; + result += var.aliveRanges.size() - 1; + } + return result; + } + /** Set the current variable defined state. */ public void setDefined(Bits newDefined) { if (alive && newDefined != state.defined) { @@ -1922,8 +2085,7 @@ } else { state.defined.incl(adr); if (cp < Character.MAX_VALUE) { - if (v.start_pc == Character.MAX_VALUE) - v.start_pc = (char)cp; + v.openRange((char)cp); } } } @@ -1933,15 +2095,15 @@ state.defined.excl(adr); if (adr < lvar.length && lvar[adr] != null && - lvar[adr].start_pc != Character.MAX_VALUE) { + lvar[adr].isLastRangeInitialized()) { LocalVar v = lvar[adr]; - char length = (char)(curPc() - v.start_pc); + char length = (char)(curCP() - v.lastRange().start_pc); if (length > 0 && length < Character.MAX_VALUE) { lvar[adr] = v.dup(); - v.length = length; + v.closeRange(length); putVar(v); } else { - v.start_pc = Character.MAX_VALUE; + v.lastRange().start_pc = Character.MAX_VALUE; } } } @@ -1951,10 +2113,10 @@ LocalVar v = lvar[adr]; if (v != null) { lvar[adr] = null; - if (v.start_pc != Character.MAX_VALUE) { - char length = (char)(curPc() - v.start_pc); + if (v.isLastRangeInitialized()) { + char length = (char)(curCP() - v.lastRange().start_pc); if (length < Character.MAX_VALUE) { - v.length = length; + v.closeRange(length); putVar(v); fillLocalVarPosition(v); } @@ -1968,8 +2130,9 @@ return; for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { TypeAnnotationPosition p = ta.position; - p.lvarOffset = new int[] { (int)lv.start_pc }; - p.lvarLength = new int[] { (int)lv.length }; + LocalVar.Range widestRange = lv.getWidestRange(); + p.lvarOffset = new int[] { (int)widestRange.start_pc }; + p.lvarLength = new int[] { (int)widestRange.length }; p.lvarIndex = new int[] { (int)lv.reg }; p.isValidOffset = true; } diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java --- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java Sat Sep 14 19:04:47 2013 +0100 @@ -24,6 +24,7 @@ */ package com.sun.tools.javac.jvm; + import java.util.*; import com.sun.tools.javac.util.*; @@ -95,10 +96,14 @@ return instance; } - /* Constant pool, reset by genClass. + /** Constant pool, reset by genClass. */ private Pool pool; + /** LVTRanges info. + */ + private LVTRanges lvtRanges; + protected Gen(Context context) { context.put(genKey, this); @@ -128,6 +133,9 @@ options.isUnset(G_CUSTOM) ? options.isSet(G) : options.isSet(G_CUSTOM, "vars"); + if (varDebugInfo) { + lvtRanges = LVTRanges.instance(context); + } genCrt = options.isSet(XJCOV); debugCode = options.isSet("debugcode"); allowInvokedynamic = target.hasInvokedynamic() || options.isSet("invokedynamic"); @@ -423,7 +431,7 @@ */ void endFinalizerGap(Env env) { if (env.info.gaps != null && env.info.gaps.length() % 2 == 1) - env.info.gaps.append(code.curPc()); + env.info.gaps.append(code.curCP()); } /** Mark end of all gaps in catch-all ranges for finalizers of environments @@ -743,10 +751,10 @@ genStat(tree, env); return; } - int startpc = code.curPc(); + int startpc = code.curCP(); genStat(tree, env); if (tree.hasTag(Tag.BLOCK)) crtFlags |= CRT_BLOCK; - code.crt.put(tree, crtFlags, startpc, code.curPc()); + code.crt.put(tree, crtFlags, startpc, code.curCP()); } /** Derived visitor method: generate code for a statement. @@ -781,9 +789,9 @@ if (trees.length() == 1) { // mark one statement with the flags genStat(trees.head, env, crtFlags | CRT_STATEMENT); } else { - int startpc = code.curPc(); + int startpc = code.curCP(); genStats(trees, env); - code.crt.put(trees, crtFlags, startpc, code.curPc()); + code.crt.put(trees, crtFlags, startpc, code.curCP()); } } @@ -806,9 +814,9 @@ */ public CondItem genCond(JCTree tree, int crtFlags) { if (!genCrt) return genCond(tree, false); - int startpc = code.curPc(); + int startpc = code.curCP(); CondItem item = genCond(tree, (crtFlags & CRT_FLOW_CONTROLLER) != 0); - code.crt.put(tree, crtFlags, startpc, code.curPc()); + code.crt.put(tree, crtFlags, startpc, code.curCP()); return item; } @@ -971,7 +979,6 @@ // definition. Env localEnv = env.dup(tree); localEnv.enclMethod = tree; - // The expected type of every return statement in this method // is the method's return type. this.pt = tree.sym.erasure(types).getReturnType(); @@ -1045,7 +1052,7 @@ code.crt.put(tree.body, CRT_BLOCK, startpcCrt, - code.curPc()); + code.curCP()); code.endScopes(0); @@ -1087,10 +1094,12 @@ : null, syms, types, - pool); + pool, + varDebugInfo ? lvtRanges : null); items = new Items(pool, code, syms, types); - if (code.debugCode) + if (code.debugCode) { System.err.println(meth + " for body " + tree); + } // If method is not static, create a new local variable address // for `this'. @@ -1111,7 +1120,7 @@ } // Get ready to generate code for method body. - int startpcCrt = genCrt ? code.curPc() : 0; + int startpcCrt = genCrt ? code.curCP() : 0; code.entryPoint(); // Suppress initial stackmap @@ -1189,14 +1198,30 @@ Chain loopDone = c.jumpFalse(); code.resolve(c.trueJumps); genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + if (varDebugInfo) { + checkLoopLocalVarRangeEnding(loop, body, + LoopLocalVarRangeEndingPoint.BEFORE_STEPS); + } code.resolve(loopEnv.info.cont); genStats(step, loopEnv); + if (varDebugInfo) { + checkLoopLocalVarRangeEnding(loop, body, + LoopLocalVarRangeEndingPoint.AFTER_STEPS); + } code.resolve(code.branch(goto_), startpc); code.resolve(loopDone); } else { genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + if (varDebugInfo) { + checkLoopLocalVarRangeEnding(loop, body, + LoopLocalVarRangeEndingPoint.BEFORE_STEPS); + } code.resolve(loopEnv.info.cont); genStats(step, loopEnv); + if (varDebugInfo) { + checkLoopLocalVarRangeEnding(loop, body, + LoopLocalVarRangeEndingPoint.AFTER_STEPS); + } CondItem c; if (cond != null) { code.statBegin(cond.pos); @@ -1210,6 +1235,44 @@ code.resolve(loopEnv.info.exit); } + private enum LoopLocalVarRangeEndingPoint { + BEFORE_STEPS, + AFTER_STEPS, + } + + /** + * Checks whether we have reached an alive range ending point for local + * variables after a loop. + * + * Local variables alive range ending point for loops varies depending + * on the loop type. The range can be closed before or after the code + * for the steps sentences has been generated. + * + * - While loops has no steps so in that case the range is closed just + * after the body of the loop. + * + * - For-like loops may have steps so as long as the steps sentences + * can possibly contain non-synthetic local variables, the alive range + * for local variables must be closed after the steps in this case. + */ + private void checkLoopLocalVarRangeEnding(JCTree loop, JCTree body, + LoopLocalVarRangeEndingPoint endingPoint) { + if (varDebugInfo && lvtRanges.containsKey(code.meth, body)) { + switch (endingPoint) { + case BEFORE_STEPS: + if (!loop.hasTag(FORLOOP)) { + code.closeAliveRanges(body); + } + break; + case AFTER_STEPS: + if (loop.hasTag(FORLOOP)) { + code.closeAliveRanges(body); + } + break; + } + } + } + public void visitForeachLoop(JCEnhancedForLoop tree) { throw new AssertionError(); // should have been removed by Lower. } @@ -1223,7 +1286,7 @@ public void visitSwitch(JCSwitch tree) { int limit = code.nextreg; Assert.check(!tree.selector.type.hasTag(CLASS)); - int startpcCrt = genCrt ? code.curPc() : 0; + int startpcCrt = genCrt ? code.curCP() : 0; Item sel = genExpr(tree.selector, syms.intType); List cases = tree.cases; if (cases.isEmpty()) { @@ -1231,13 +1294,13 @@ sel.load().drop(); if (genCrt) code.crt.put(TreeInfo.skipParens(tree.selector), - CRT_FLOW_CONTROLLER, startpcCrt, code.curPc()); + CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); } else { // We are seeing a nonempty switch. sel.load(); if (genCrt) code.crt.put(TreeInfo.skipParens(tree.selector), - CRT_FLOW_CONTROLLER, startpcCrt, code.curPc()); + CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); Env switchEnv = env.dup(tree, new GenContext()); switchEnv.info.isSwitch = true; @@ -1278,10 +1341,10 @@ ? tableswitch : lookupswitch; - int startpc = code.curPc(); // the position of the selector operation + int startpc = code.curCP(); // the position of the selector operation code.emitop0(opcode); code.align(4); - int tableBase = code.curPc(); // the start of the jump table + int tableBase = code.curCP(); // the start of the jump table int[] offsets = null; // a table of offsets for a lookupswitch code.emit4(-1); // leave space for default offset if (opcode == tableswitch) { @@ -1323,6 +1386,9 @@ // Generate code for the statements in this case. genStats(c.stats, switchEnv, CRT_FLOW_TARGET); + if (varDebugInfo && lvtRanges.containsKey(code.meth, c.stats.last())) { + code.closeAliveRanges(c.stats.last()); + } } // Resolve all breaks. @@ -1402,7 +1468,7 @@ void gen() { genLast(); Assert.check(syncEnv.info.gaps.length() % 2 == 0); - syncEnv.info.gaps.append(code.curPc()); + syncEnv.info.gaps.append(code.curCP()); } void genLast() { if (code.isAlive()) { @@ -1441,10 +1507,10 @@ jsrState); } Assert.check(tryEnv.info.gaps.length() % 2 == 0); - tryEnv.info.gaps.append(code.curPc()); + tryEnv.info.gaps.append(code.curCP()); } else { Assert.check(tryEnv.info.gaps.length() % 2 == 0); - tryEnv.info.gaps.append(code.curPc()); + tryEnv.info.gaps.append(code.curCP()); genLast(); } } @@ -1467,10 +1533,10 @@ */ void genTry(JCTree body, List catchers, Env env) { int limit = code.nextreg; - int startpc = code.curPc(); + int startpc = code.curCP(); Code.State stateTry = code.state.dup(); genStat(body, env, CRT_BLOCK); - int endpc = code.curPc(); + int endpc = code.curCP(); boolean hasFinalizer = env.info.finalize != null && env.info.finalize.hasFinalizer(); @@ -1479,6 +1545,9 @@ genFinalizer(env); code.statBegin(TreeInfo.endPos(env.tree)); Chain exitChain = code.branch(goto_); + if (varDebugInfo && lvtRanges.containsKey(code.meth, body)) { + code.closeAliveRanges(body); + } endFinalizerGap(env); if (startpc != endpc) for (List l = catchers; l.nonEmpty(); l = l.tail) { // start off with exception on stack @@ -1573,7 +1642,7 @@ int catchType = makeRef(tree.pos(), subCatch.type); int end = gaps.head.intValue(); registerCatch(tree.pos(), - startpc, end, code.curPc(), + startpc, end, code.curCP(), catchType); if (subCatch.type.isAnnotated()) { // All compounds share the same position, simply update the @@ -1589,7 +1658,7 @@ for (JCExpression subCatch : subClauses) { int catchType = makeRef(tree.pos(), subCatch.type); registerCatch(tree.pos(), - startpc, endpc, code.curPc(), + startpc, endpc, code.curCP(), catchType); if (subCatch.type.isAnnotated()) { // All compounds share the same position, simply update the @@ -1732,11 +1801,19 @@ code.resolve(c.trueJumps); genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET); thenExit = code.branch(goto_); + if (varDebugInfo && lvtRanges.containsKey(code.meth, tree.thenpart)) { + code.closeAliveRanges(tree.thenpart, + thenExit != null && tree.elsepart == null ? thenExit.pc : code.cp); + } } if (elseChain != null) { code.resolve(elseChain); - if (tree.elsepart != null) + if (tree.elsepart != null) { genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); + if (varDebugInfo && lvtRanges.containsKey(code.meth, tree.elsepart)) { + code.closeAliveRanges(tree.elsepart); + } + } } code.resolve(thenExit); code.endScopes(limit); @@ -1830,20 +1907,20 @@ Chain elseChain = c.jumpFalse(); if (!c.isFalse()) { code.resolve(c.trueJumps); - int startpc = genCrt ? code.curPc() : 0; + int startpc = genCrt ? code.curCP() : 0; genExpr(tree.truepart, pt).load(); code.state.forceStackTop(tree.type); if (genCrt) code.crt.put(tree.truepart, CRT_FLOW_TARGET, - startpc, code.curPc()); + startpc, code.curCP()); thenExit = code.branch(goto_); } if (elseChain != null) { code.resolve(elseChain); - int startpc = genCrt ? code.curPc() : 0; + int startpc = genCrt ? code.curCP() : 0; genExpr(tree.falsepart, pt).load(); code.state.forceStackTop(tree.type); if (genCrt) code.crt.put(tree.falsepart, CRT_FLOW_TARGET, - startpc, code.curPc()); + startpc, code.curCP()); } code.resolve(thenExit); result = items.makeStackItem(pt); @@ -2423,6 +2500,19 @@ new Env(cdef, new GenContext()); localEnv.toplevel = env.toplevel; localEnv.enclClass = cdef; + + /* We must not analyze synthetic methods + */ + if (varDebugInfo && (cdef.sym.flags() & SYNTHETIC) == 0) { + try { + LVTAssignAnalyzer lvtAssignAnalyzer = LVTAssignAnalyzer.make( + lvtRanges, syms, names); + lvtAssignAnalyzer.analyzeTree(localEnv); + } catch (Throwable e) { + throw e; + } + } + for (List l = cdef.defs; l.nonEmpty(); l = l.tail) { genDef(l.head, localEnv); } @@ -2507,4 +2597,311 @@ cont = Code.mergeChains(c, cont); } } + + static class LVTAssignAnalyzer + extends Flow.AbstractAssignAnalyzer { + + final LVTBits lvtInits; + final LVTRanges lvtRanges; + + /* This class is anchored to a context dependent tree. The tree can + * vary inside the same instruction for example in the switch instruction + * the same FlowBits instance can be anchored to the whole tree, or + * to a given case. The aim is to always anchor the bits to the tree + * capable of closing a DA range. + */ + static class LVTBits extends Bits { + + enum BitsOpKind { + INIT, + CLEAR, + INCL_BIT, + EXCL_BIT, + ASSIGN, + AND_SET, + OR_SET, + DIFF_SET, + XOR_SET, + INCL_RANGE, + EXCL_RANGE, + } + + JCTree currentTree; + LVTAssignAnalyzer analyzer; + private int[] oldBits = null; + BitsState stateBeforeOp; + + LVTBits() { + super(false); + } + + LVTBits(int[] bits, BitsState initState) { + super(bits, initState); + } + + @Override + public void clear() { + generalOp(null, -1, BitsOpKind.CLEAR); + } + + @Override + protected void internalReset() { + super.internalReset(); + oldBits = null; + } + + @Override + public Bits assign(Bits someBits) { + // bits can be null + oldBits = bits; + stateBeforeOp = currentState; + super.assign(someBits); + changed(); + return this; + } + + @Override + public void excludeFrom(int start) { + generalOp(null, start, BitsOpKind.EXCL_RANGE); + } + + @Override + public void excl(int x) { + Assert.check(x >= 0); + generalOp(null, x, BitsOpKind.EXCL_BIT); + } + + @Override + public Bits andSet(Bits xs) { + return generalOp(xs, -1, BitsOpKind.AND_SET); + } + + @Override + public Bits orSet(Bits xs) { + return generalOp(xs, -1, BitsOpKind.OR_SET); + } + + @Override + public Bits diffSet(Bits xs) { + return generalOp(xs, -1, BitsOpKind.DIFF_SET); + } + + @Override + public Bits xorSet(Bits xs) { + return generalOp(xs, -1, BitsOpKind.XOR_SET); + } + + private Bits generalOp(Bits xs, int i, BitsOpKind opKind) { + Assert.check(currentState != BitsState.UNKNOWN); + oldBits = dupBits(); + stateBeforeOp = currentState; + switch (opKind) { + case AND_SET: + super.andSet(xs); + break; + case OR_SET: + super.orSet(xs); + break; + case XOR_SET: + super.xorSet(xs); + break; + case DIFF_SET: + super.diffSet(xs); + break; + case CLEAR: + super.clear(); + break; + case EXCL_BIT: + super.excl(i); + break; + case EXCL_RANGE: + super.excludeFrom(i); + break; + } + changed(); + return this; + } + + /* The tree we need to anchor the bits instance to. + */ + LVTBits at(JCTree tree) { + this.currentTree = tree; + return this; + } + + /* If the instance should be changed but the tree is not a closing + * tree then a reset is needed or the former tree can mistakingly be + * used. + */ + LVTBits resetTree() { + this.currentTree = null; + return this; + } + + /** This method will be called after any operation that causes a change to + * the bits. Subclasses can thus override it in order to extract information + * from the changes produced to the bits by the given operation. + */ + public void changed() { + if (currentTree != null && + stateBeforeOp != BitsState.UNKNOWN && + trackTree(currentTree)) { + List locals = + analyzer.lvtRanges + .getVars(analyzer.currentMethod, currentTree); + locals = locals != null ? + locals : List.nil(); + for (JCVariableDecl vardecl : analyzer.vardecls) { + //once the first is null, the rest will be so. + if (vardecl == null) { + break; + } + if (trackVar(vardecl.sym) && bitChanged(vardecl.sym.adr)) { + locals = locals.prepend(vardecl.sym); + } + } + if (!locals.isEmpty()) { + analyzer.lvtRanges.setEntry(analyzer.currentMethod, + currentTree, locals); + } + } + } + + boolean bitChanged(int x) { + boolean isMemberOfBits = isMember(x); + int[] tmp = bits; + bits = oldBits; + boolean isMemberOfOldBits = isMember(x); + bits = tmp; + return (!isMemberOfBits && isMemberOfOldBits); + } + + boolean trackVar(VarSymbol var) { + return (var.owner.kind == MTH && + (var.flags() & (PARAMETER | HASINIT)) == 0 && + analyzer.trackable(var)); + } + + boolean trackTree(JCTree tree) { + switch (tree.getTag()) { + // of course a method closes the alive range of a local variable. + case METHODDEF: + // for while loops we want only the body + case WHILELOOP: + return false; + } + return true; + } + + } + + public class LVTAssignPendingExit extends Flow.AssignAnalyzer.AssignPendingExit { + + LVTAssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { + super(tree, inits, uninits); + } + + @Override + public void resolveJump(JCTree tree) { + lvtInits.at(tree); + super.resolveJump(tree); + } + } + + private LVTAssignAnalyzer(LVTRanges lvtRanges, Symtab syms, Names names) { + super(new LVTBits(), syms, names); + lvtInits = (LVTBits)inits; + this.lvtRanges = lvtRanges; + } + + public static LVTAssignAnalyzer make(LVTRanges lvtRanges, Symtab syms, Names names) { + LVTAssignAnalyzer result = new LVTAssignAnalyzer(lvtRanges, syms, names); + result.lvtInits.analyzer = result; + return result; + } + + @Override + protected void markDead(JCTree tree) { + lvtInits.at(tree).inclRange(returnadr, nextadr); + super.markDead(tree); + } + + @Override + protected void merge(JCTree tree) { + lvtInits.at(tree); + super.merge(tree); + } + + boolean isSyntheticOrMandated(Symbol sym) { + return (sym.flags() & (SYNTHETIC | MANDATED)) != 0; + } + + @Override + protected boolean trackable(VarSymbol sym) { + if (isSyntheticOrMandated(sym)) { + //fast check to avoid tracking synthetic or mandated variables + return false; + } + return super.trackable(sym); + } + + @Override + protected void initParam(JCVariableDecl def) { + if (!isSyntheticOrMandated(def.sym)) { + super.initParam(def); + } + } + + @Override + protected void assignToInits(JCTree tree, Bits bits) { + lvtInits.at(tree); + lvtInits.assign(bits); + } + + @Override + protected void andSetInits(JCTree tree, Bits bits) { + lvtInits.at(tree); + lvtInits.andSet(bits); + } + + @Override + protected void orSetInits(JCTree tree, Bits bits) { + lvtInits.at(tree); + lvtInits.orSet(bits); + } + + @Override + protected void exclVarFromInits(JCTree tree, int adr) { + lvtInits.at(tree); + lvtInits.excl(adr); + } + + @Override + protected LVTAssignPendingExit createNewPendingExit(JCTree tree, Bits inits, Bits uninits) { + return new LVTAssignPendingExit(tree, inits, uninits); + } + + MethodSymbol currentMethod; + + @Override + public void visitMethodDef(JCMethodDecl tree) { + if ((tree.sym.flags() & (SYNTHETIC | GENERATEDCONSTR)) != 0) { + return; + } + if (tree.name.equals(names.clinit)) { + return; + } + boolean enumClass = (tree.sym.owner.flags() & ENUM) != 0; + if (enumClass && + (tree.name.equals(names.valueOf) || + tree.name.equals(names.values) || + tree.name.equals(names.init))) { + return; + } + currentMethod = tree.sym; + super.visitMethodDef(tree); + } + + } + } diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/jvm/Items.java --- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Items.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Items.java Sat Sep 14 19:04:47 2013 +0100 @@ -789,18 +789,18 @@ Chain jumpTrue() { if (tree == null) return Code.mergeChains(trueJumps, code.branch(opcode)); // we should proceed further in -Xjcov mode only - int startpc = code.curPc(); + int startpc = code.curCP(); Chain c = Code.mergeChains(trueJumps, code.branch(opcode)); - code.crt.put(tree, CRTable.CRT_BRANCH_TRUE, startpc, code.curPc()); + code.crt.put(tree, CRTable.CRT_BRANCH_TRUE, startpc, code.curCP()); return c; } Chain jumpFalse() { if (tree == null) return Code.mergeChains(falseJumps, code.branch(Code.negate(opcode))); // we should proceed further in -Xjcov mode only - int startpc = code.curPc(); + int startpc = code.curCP(); Chain c = Code.mergeChains(falseJumps, code.branch(Code.negate(opcode))); - code.crt.put(tree, CRTable.CRT_BRANCH_FALSE, startpc, code.curPc()); + code.crt.put(tree, CRTable.CRT_BRANCH_FALSE, startpc, code.curCP()); return c; } diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/jvm/LVTRanges.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/LVTRanges.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.jvm; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.WeakHashMap; + +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; + +/** This class contains a one to many relation between a tree and a set of variables. + * The relation implies that the given tree closes the DA (definite assignment) + * range for the set of variables. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class LVTRanges { + /** The context key for the LVT ranges. */ + protected static final Context.Key lvtRangesKey = new Context.Key<>(); + + /** Get the LVTRanges instance for this context. */ + public static LVTRanges instance(Context context) { + LVTRanges instance = context.get(lvtRangesKey); + if (instance == null) { + instance = new LVTRanges(context); + } + return instance; + } + + private static final long serialVersionUID = 1812267524140424433L; + + protected Context context; + + protected Map>> + aliveRangeClosingTrees = new WeakHashMap<>(); + + public LVTRanges(Context context) { + this.context = context; + context.put(lvtRangesKey, this); + } + + public List getVars(MethodSymbol method, JCTree tree) { + Map> varMap = aliveRangeClosingTrees.get(method); + return (varMap != null) ? varMap.get(tree) : null; + } + + public boolean containsKey(MethodSymbol method, JCTree tree) { + Map> varMap = aliveRangeClosingTrees.get(method); + if (varMap == null) { + return false; + } + return varMap.containsKey(tree); + } + + public void setEntry(MethodSymbol method, JCTree tree, List vars) { + Map> varMap = aliveRangeClosingTrees.get(method); + if (varMap != null) { + varMap.put(tree, vars); + } else { + varMap = new WeakHashMap<>(); + varMap.put(tree, vars); + aliveRangeClosingTrees.put(method, varMap); + } + } + + public List removeEntry(MethodSymbol method, JCTree tree) { + Map> varMap = aliveRangeClosingTrees.get(method); + if (varMap != null) { + List result = varMap.remove(tree); + if (varMap.isEmpty()) { + aliveRangeClosingTrees.remove(method); + } + return result; + } + return null; + } + + /* This method should be used for debugging LVT related issues. + */ + @Override + public String toString() { + String result = ""; + for (Entry>> mainEntry: aliveRangeClosingTrees.entrySet()) { + result += "Method: \n" + mainEntry.getKey().flatName() + "\n"; + int i = 1; + for (Entry> treeEntry: mainEntry.getValue().entrySet()) { + result += " Tree " + i + ": \n" + treeEntry.getKey().toString() + "\n"; + result += " Variables closed:\n"; + for (VarSymbol var: treeEntry.getValue()) { + result += " " + var.toString(); + } + result += "\n"; + i++; + } + } + return result; + } + +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java --- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java Sat Sep 14 19:04:47 2013 +0100 @@ -890,7 +890,7 @@ /** Create a value parameter tree from its name, type, and owner. */ public JCVariableDecl Param(Name name, Type argtype, Symbol owner) { - return VarDef(new VarSymbol(0, name, argtype, owner), null); + return VarDef(new VarSymbol(PARAMETER, name, argtype, owner), null); } /** Create a a list of value parameter trees x0, ..., xn from a list of diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/src/share/classes/com/sun/tools/javac/util/Bits.java --- a/langtools/src/share/classes/com/sun/tools/javac/util/Bits.java Sat Sep 14 15:23:21 2013 +0100 +++ b/langtools/src/share/classes/com/sun/tools/javac/util/Bits.java Sat Sep 14 19:04:47 2013 +0100 @@ -27,8 +27,6 @@ import java.util.Arrays; -import static com.sun.tools.javac.util.Bits.BitsOpKind.*; - /** A class for extensible, mutable bit sets. * *

This is NOT part of any supported API. @@ -38,20 +36,6 @@ */ public class Bits { - public enum BitsOpKind { - INIT, - CLEAR, - INCL_BIT, - EXCL_BIT, - ASSIGN, - AND_SET, - OR_SET, - DIFF_SET, - XOR_SET, - INCL_RANGE, - EXCL_RANGE, - } - // ____________ reset _________ // / UNKNOWN \ <-------- / UNINIT \ // \____________/ | \_________/ @@ -64,11 +48,14 @@ // | | // ----------- // any - private enum BitsState { + protected enum BitsState { /* A Bits instance is in UNKNOWN state if it has been explicitly reset. * It is possible to get to this state from any other by calling the * reset method. An instance in the UNKNOWN state can pass to the * NORMAL state after being assigned another Bits instance. + * + * Bits instances are final fields in Flow so the UNKNOWN state models + * the null assignment. */ UNKNOWN, /* A Bits instance is in UNINIT when it is created with the default @@ -103,13 +90,9 @@ public int[] bits = null; // This field will store last version of bits after every change. - public int[] oldBits = null; - - public BitsOpKind lastOperation = null; - private static final int[] unassignedBits = new int[0]; - private BitsState currentState; + protected BitsState currentState; /** Construct an initially empty set. */ @@ -127,27 +110,20 @@ /** Construct a set consisting initially of given bit vector. */ - private Bits(int[] bits, BitsState initState) { + protected Bits(int[] bits, BitsState initState) { this.bits = bits; this.currentState = initState; switch (initState) { case UNKNOWN: - reset(); //this will also set current state; + this.bits = null; break; case NORMAL: Assert.check(bits != unassignedBits); - lastOperation = INIT; break; } } - /** This method will be called after any operation that causes a change to - * the bits. Subclasses can thus override it in order to extract information - * from the changes produced to the bits by the given operation. - */ - public void changed() {} - - private void sizeTo(int len) { + protected void sizeTo(int len) { if (bits.length < len) { bits = Arrays.copyOf(bits, len); } @@ -157,16 +133,18 @@ */ public void clear() { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = CLEAR; - for (int i = 0; i < bits.length; i++) bits[i] = 0; - changed(); + for (int i = 0; i < bits.length; i++) { + bits[i] = 0; + } currentState = BitsState.NORMAL; } public void reset() { + internalReset(); + } + + protected void internalReset() { bits = null; - oldBits = null; currentState = BitsState.UNKNOWN; } @@ -175,40 +153,40 @@ } public Bits assign(Bits someBits) { - lastOperation = ASSIGN; - oldBits = bits; bits = someBits.dup().bits; - changed(); currentState = BitsState.NORMAL; return this; } /** Return a copy of this set. */ - private Bits dup() { + public Bits dup() { Assert.check(currentState != BitsState.UNKNOWN); Bits tmp = new Bits(); - if (currentState != BitsState.NORMAL) { - tmp.bits = bits; - } else { - tmp.bits = new int[bits.length]; - System.arraycopy(bits, 0, tmp.bits, 0, bits.length); - } + tmp.bits = dupBits(); currentState = BitsState.NORMAL; return tmp; } + protected int[] dupBits() { + int [] result; + if (currentState != BitsState.NORMAL) { + result = bits; + } else { + result = new int[bits.length]; + System.arraycopy(bits, 0, result, 0, bits.length); + } + return result; + } + /** Include x in this set. */ public void incl(int x) { Assert.check(currentState != BitsState.UNKNOWN); - Assert.check(x >= 0); - oldBits = bits; - lastOperation = INCL_BIT; + Assert.check(x >= 0, "Value of x " + x); sizeTo((x >>> wordshift) + 1); bits[x >>> wordshift] = bits[x >>> wordshift] | (1 << (x & wordmask)); - changed(); currentState = BitsState.NORMAL; } @@ -217,14 +195,11 @@ */ public void inclRange(int start, int limit) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = INCL_RANGE; sizeTo((limit >>> wordshift) + 1); for (int x = start; x < limit; x++) { bits[x >>> wordshift] = bits[x >>> wordshift] | (1 << (x & wordmask)); } - changed(); currentState = BitsState.NORMAL; } @@ -232,13 +207,10 @@ */ public void excludeFrom(int start) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = EXCL_RANGE; Bits temp = new Bits(); temp.sizeTo(bits.length); temp.inclRange(0, start); internalAndSet(temp); - changed(); currentState = BitsState.NORMAL; } @@ -247,12 +219,9 @@ public void excl(int x) { Assert.check(currentState != BitsState.UNKNOWN); Assert.check(x >= 0); - oldBits = bits; - lastOperation = EXCL_BIT; sizeTo((x >>> wordshift) + 1); bits[x >>> wordshift] = bits[x >>> wordshift] & ~(1 << (x & wordmask)); - changed(); currentState = BitsState.NORMAL; } @@ -269,15 +238,12 @@ */ public Bits andSet(Bits xs) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = AND_SET; internalAndSet(xs); - changed(); currentState = BitsState.NORMAL; return this; } - private void internalAndSet(Bits xs) { + protected void internalAndSet(Bits xs) { Assert.check(currentState != BitsState.UNKNOWN); sizeTo(xs.bits.length); for (int i = 0; i < xs.bits.length; i++) { @@ -289,13 +255,10 @@ */ public Bits orSet(Bits xs) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = OR_SET; sizeTo(xs.bits.length); for (int i = 0; i < xs.bits.length; i++) { bits[i] = bits[i] | xs.bits[i]; } - changed(); currentState = BitsState.NORMAL; return this; } @@ -304,14 +267,11 @@ */ public Bits diffSet(Bits xs) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = DIFF_SET; for (int i = 0; i < bits.length; i++) { if (i < xs.bits.length) { bits[i] = bits[i] & ~xs.bits[i]; } } - changed(); currentState = BitsState.NORMAL; return this; } @@ -320,13 +280,10 @@ */ public Bits xorSet(Bits xs) { Assert.check(currentState != BitsState.UNKNOWN); - oldBits = bits; - lastOperation = XOR_SET; sizeTo(xs.bits.length); for (int i = 0; i < xs.bits.length; i++) { bits[i] = bits[i] ^ xs.bits[i]; } - changed(); currentState = BitsState.NORMAL; return this; } @@ -336,7 +293,9 @@ */ private static int trailingZeroBits(int x) { Assert.check(wordlen == 32); - if (x == 0) return 32; + if (x == 0) { + return 32; + } int n = 1; if ((x & 0xffff) == 0) { n += 16; x >>>= 16; } if ((x & 0x00ff) == 0) { n += 8; x >>>= 8; } @@ -355,24 +314,31 @@ public int nextBit(int x) { Assert.check(currentState != BitsState.UNKNOWN); int windex = x >>> wordshift; - if (windex >= bits.length) return -1; + if (windex >= bits.length) { + return -1; + } int word = bits[windex] & ~((1 << (x & wordmask))-1); while (true) { - if (word != 0) + if (word != 0) { return (windex << wordshift) + trailingZeroBits(word); + } windex++; - if (windex >= bits.length) return -1; + if (windex >= bits.length) { + return -1; + } word = bits[windex]; } } /** a string representation of this set. */ + @Override public String toString() { - if (bits.length > 0) { + if (bits != null && bits.length > 0) { char[] digits = new char[bits.length * wordlen]; - for (int i = 0; i < bits.length * wordlen; i++) + for (int i = 0; i < bits.length * wordlen; i++) { digits[i] = isMember(i) ? '1' : '0'; + } return new String(digits); } else { return "[]"; @@ -396,6 +362,8 @@ System.out.println("found " + i); count ++; } - if (count != 125) throw new Error(); + if (count != 125) { + throw new Error(); + } } } diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/AliveRanges.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/AliveRanges.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.lang.annotation.*; + +@Repeatable(AliveRanges.class) +@Target({ElementType.METHOD}) +@interface AliveRange { + String varName(); + int bytecodeStart(); + int bytecodeLength(); +} + +@Target({ElementType.METHOD}) +@interface AliveRanges {AliveRange[] value();} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/LVTHarness.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/LVTHarness.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 7047734 + * @summary The LVT is not generated correctly during some try/catch scenarios + * @library /tools/javac/lib + * @build JavacTestingAbstractProcessor LVTHarness + * @run main LVTHarness + */ + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.Set; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import com.sun.source.util.JavacTask; +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.ConstantPool; +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.ConstantPool.InvalidIndex; +import com.sun.tools.classfile.ConstantPool.UnexpectedEntry; +import com.sun.tools.classfile.Descriptor.InvalidDescriptor; +import com.sun.tools.classfile.LocalVariableTable_attribute; +import com.sun.tools.classfile.Method; + +import static javax.tools.StandardLocation.*; +import static com.sun.tools.classfile.LocalVariableTable_attribute.Entry; + +public class LVTHarness { + + static int nerrors = 0; + + static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); + + public static void main(String[] args) throws Exception { + fm.setLocation(SOURCE_PATH, + Arrays.asList(new File(System.getProperty("test.src"), "tests"))); + for (JavaFileObject jfo : fm.list(SOURCE_PATH, "", + Collections.singleton(JavaFileObject.Kind.SOURCE), true)) { + new LVTHarness(jfo).check(); + } + if (nerrors > 0) { + throw new AssertionError("Errors were found"); + } + } + + + JavaFileObject jfo; + Map aliveRangeMap = + new HashMap(); + Set declaredKeys = new HashSet<>(); + List seenAliveRanges = new ArrayList<>(); + + protected LVTHarness(JavaFileObject jfo) { + this.jfo = jfo; + } + + protected void check() throws Exception { + JavacTask ct = (JavacTask)comp.getTask(null, fm, null, Arrays.asList("-g"), + null, Arrays.asList(jfo)); + System.err.println("compiling code " + jfo.toString()); + ct.setProcessors(Collections.singleton(new AliveRangeFinder())); + if (!ct.call()) { + throw new AssertionError("Error during compilation"); + } + + checkClassFile(new File(jfo.getName().replace(".java", ".class"))); + + //check all candidates have been used up + for (Map.Entry entry : aliveRangeMap.entrySet()) { + if (!seenAliveRanges.contains(entry.getKey())) { + error("Redundant @AliveRanges annotation on method " + + entry.getKey().elem); + } + } + } + + void checkClassFile(File file) + throws IOException, ConstantPoolException, InvalidDescriptor { + ClassFile classFile = ClassFile.read(file); + ConstantPool constantPool = classFile.constant_pool; + + //lets get all the methods in the class file. + for (Method method : classFile.methods) { + for (ElementKey elementKey: aliveRangeMap.keySet()) { + String methodDesc = method.getName(constantPool) + + method.descriptor.getParameterTypes(constantPool); + if (methodDesc.equals(elementKey.elem.toString())) { + checkMethod(constantPool, method, aliveRangeMap.get(elementKey)); + seenAliveRanges.add(elementKey); + } + } + } + } + + void checkMethod(ConstantPool constantPool, Method method, AliveRanges ranges) + throws InvalidIndex, UnexpectedEntry { + Code_attribute code = (Code_attribute) method.attributes.get(Attribute.Code); + LocalVariableTable_attribute lvt = + (LocalVariableTable_attribute) (code.attributes.get(Attribute.LocalVariableTable)); + List infoFromRanges = convertToStringList(ranges); + List infoFromLVT = convertToStringList(constantPool, lvt); + + // infoFromRanges most be contained in infoFromLVT + int i = 0; + int j = 0; + while (i < infoFromRanges.size() && j < infoFromLVT.size()) { + int comparison = infoFromRanges.get(i).compareTo(infoFromLVT.get(j)); + if (comparison == 0) { + i++; j++; + } else if (comparison > 0) { + j++; + } else { + break; + } + } + + if (i < infoFromRanges.size()) { + error(infoFromLVT, infoFromRanges); + } + } + + List convertToStringList(AliveRanges ranges) { + List result = new ArrayList<>(); + for (Annotation anno : ranges.value()) { + AliveRange range = (AliveRange)anno; + String str = formatLocalVariableData(range.varName(), + range.bytecodeStart(), range.bytecodeLength()); + result.add(str); + } + Collections.sort(result); + return result; + } + + List convertToStringList(ConstantPool constantPool, + LocalVariableTable_attribute lvt) throws InvalidIndex, UnexpectedEntry { + List result = new ArrayList<>(); + for (Entry entry : lvt.local_variable_table) { + String str = formatLocalVariableData(constantPool.getUTF8Value(entry.name_index), + entry.start_pc, entry.length); + result.add(str); + } + Collections.sort(result); + return result; + } + + String formatLocalVariableData(String varName, int start, int length) { + StringBuilder sb = new StringBuilder() + .append("var name: ").append(varName) + .append(" start: ").append(start) + .append(" length: ").append(length); + return sb.toString(); + } + + protected void error(List infoFromLVT, List infoFromRanges) { + nerrors++; + System.err.printf("Error occurred while checking file: %s\n", jfo.getName()); + System.err.println("The range info from the annotations is"); + printStringListToErrOutput(infoFromRanges); + System.err.println(); + System.err.println("And the range info from the class file is"); + printStringListToErrOutput(infoFromLVT); + System.err.println(); + } + + void printStringListToErrOutput(List list) { + for (String s : list) { + System.err.println("\t" + s); + } + } + + protected void error(String msg) { + nerrors++; + System.err.printf("Error occurred while checking file: %s\nreason: %s\n", + jfo.getName(), msg); + } + + class AliveRangeFinder extends JavacTestingAbstractProcessor { + + @Override + public boolean process(Set annotations, + RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) + return true; + + TypeElement aliveRangeAnno = elements.getTypeElement("AliveRanges"); + + if (!annotations.contains(aliveRangeAnno)) { + error("no @AliveRanges annotation found in test class"); + } + + for (Element elem: roundEnv.getElementsAnnotatedWith(aliveRangeAnno)) { + Annotation annotation = elem.getAnnotation(AliveRanges.class); + aliveRangeMap.put(new ElementKey(elem), (AliveRanges)annotation); + } + return true; + } + } + + class ElementKey { + + String key; + Element elem; + + public ElementKey(Element elem) { + this.elem = elem; + this.key = computeKey(elem); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ElementKey) { + ElementKey other = (ElementKey)obj; + return other.key.equals(key); + } + return false; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + String computeKey(Element e) { + StringBuilder buf = new StringBuilder(); + while (e != null) { + buf.append(e.toString()); + e = e.getEnclosingElement(); + } + buf.append(jfo.getName()); + return buf.toString(); + } + + @Override + public String toString() { + return "Key{" + key + "}"; + } + } + +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseConditional.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseConditional.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,16 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseConditional { + + @AliveRange(varName="o", bytecodeStart=5, bytecodeLength=33) + @AliveRange(varName="oo", bytecodeStart=23, bytecodeLength=15) + void m(String[] args) { + Boolean o; + Boolean oo = ((o = Boolean.TRUE).booleanValue()) ? + o = Boolean.TRUE : + Boolean.FALSE; + oo.hashCode(); + o = Boolean.FALSE; + o.hashCode(); + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseDoLoop.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseDoLoop.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,15 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseDoLoop { + + @AliveRange(varName="o", bytecodeStart=3, bytecodeLength=15) + @AliveRange(varName="args", bytecodeStart=0, bytecodeLength=18) + void m(String[] args) { + Object o; + do { + o = ""; + o.hashCode(); + } while (args[0] != null); + o = ""; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseFor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseFor.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,27 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseFor { + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=24, bytecodeLength=1) + void m1(String[] args) { + Object o; + for (int i = 0; i < 5; i++) { + o = ""; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=24, bytecodeLength=1) + void m2(String[] args) { + Object o; + for (int i = 0; i < 5; i++) { + o = ""; + o.hashCode(); + continue; + } + o = ""; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseForEach.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseForEach.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,15 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseForEach { + + @AliveRange(varName="o", bytecodeStart=25, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=39, bytecodeLength=1) + void m(String[] args) { + Object o; + for (String s : args) { + o = ""; + o.hashCode(); + } + o = ""; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseIf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseIf.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,61 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseIf { + + @AliveRange(varName="o", bytecodeStart=9, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=17, bytecodeLength=1) + void m0(String[] args) { + Object o; + if (args[0] != null) { + o = ""; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=18, bytecodeLength=1) + void m1() { + Object o; + int i = 5; + if (i == 5) { + o = ""; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=18, bytecodeLength=1) + void m2() { + Object o; + int i = 5; + if (!(i == 5)) { + o = ""; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=15, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=23, bytecodeLength=1) + void m3(String[] args) { + Object o; + if (args[0] != null && args[1] != null) { + o = ""; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=15, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=23, bytecodeLength=1) + void m4(String[] args) { + Object o; + if (args[0] != null || args[1] != null) { + o = ""; + o.hashCode(); + } + o = ""; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseIfElse.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseIfElse.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,48 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseIfElse { + + @AliveRange(varName="o", bytecodeStart=9, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=20, bytecodeLength=9) + void m0(String[] args) { + Object o; + if (args[0] != null) { + o = "then"; + o.hashCode(); + } else { + o = "else"; + o.hashCode(); + } + o = "finish"; + } + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=21, bytecodeLength=9) + void m1() { + Object o; + int i = 5; + if (i == 5) { + o = "then"; + o.hashCode(); + } else { + o = "else"; + o.hashCode(); + } + o = "finish"; + } + + @AliveRange(varName="o", bytecodeStart=10, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=21, bytecodeLength=9) + void m2(String[] args) { + Object o; + int i = 5; + if (i != 5) { + o = "then"; + o.hashCode(); + } else { + o = "else"; + o.hashCode(); + } + o = "finish"; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseSwitch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseSwitch.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,73 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseSwitch { + + @AliveRange(varName="o", bytecodeStart=31, bytecodeLength=16) + @AliveRange(varName="o", bytecodeStart=50, bytecodeLength=15) + @AliveRange(varName="o", bytecodeStart=68, bytecodeLength=1) + @AliveRange(varName="oo", bytecodeStart=39, bytecodeLength=26) + @AliveRange(varName="uu", bytecodeStart=59, bytecodeLength=6) + void m1(String[] args) { + Object o; + switch (args.length) { + case 0: + o = "0"; + o.hashCode(); + Object oo = "oo"; + oo.hashCode(); + break; + case 1: + o = "1"; + o.hashCode(); + Object uu = "uu"; + uu.hashCode(); + break; + } + o = "return"; + } + + @AliveRange(varName="o", bytecodeStart=95, bytecodeLength=18) + @AliveRange(varName="o", bytecodeStart=116, bytecodeLength=15) + @AliveRange(varName="o", bytecodeStart=134, bytecodeLength=1) + @AliveRange(varName="oo", bytecodeStart=104, bytecodeLength=27) + @AliveRange(varName="uu", bytecodeStart=125, bytecodeLength=6) + void m2(String[] args) { + Object o; + switch (args[0]) { + case "string0": + o = "0"; + o.hashCode(); + Object oo = "oo"; + oo.hashCode(); + break; + case "string1": + o = "1"; + o.hashCode(); + Object uu = "uu"; + uu.hashCode(); + break; + } + o = "return"; + } + + @AliveRange(varName="o", bytecodeStart=31, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=42, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=53, bytecodeLength=9) + void m3(String[] args) { + Object o; + switch (args.length) { + case 0: + o = "0"; + o.hashCode(); + break; + case 1: + o = "1"; + o.hashCode(); + break; + default: + o = "default"; + o.hashCode(); + } + o = "finish"; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseTry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseTry.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,76 @@ +/* /nodynamiccopyright/ */ + +import java.io.BufferedReader; +import java.io.FileReader; + +public class TestCaseTry { + + @AliveRange(varName="o", bytecodeStart=3, bytecodeLength=8) + @AliveRange(varName="o", bytecodeStart=15, bytecodeLength=1) + void m0(String[] args) { + Object o; + try { + o = ""; + o.hashCode(); + } catch (RuntimeException e) {} + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=3, bytecodeLength=16) + @AliveRange(varName="o", bytecodeStart=23, bytecodeLength=23) + void m1() { + Object o; + try { + o = ""; + o.hashCode(); + } catch (RuntimeException e) { + } + finally { + o = "finally"; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=3, bytecodeLength=16) + @AliveRange(varName="o", bytecodeStart=23, bytecodeLength=31) + void m2() { + Object o; + try { + o = ""; + o.hashCode(); + } catch (RuntimeException e) { + o = "catch"; + o.hashCode(); + } + finally { + o = "finally"; + o.hashCode(); + } + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=22, bytecodeLength=38) + @AliveRange(varName="o", bytecodeStart=103, bytecodeLength=8) + void m3() { + Object o; + try (BufferedReader br = + new BufferedReader(new FileReader("aFile"))) { + o = "inside try"; + o.hashCode(); + } catch (Exception e) {} + o = ""; + } + + @AliveRange(varName="o", bytecodeStart=12, bytecodeLength=96) + @AliveRange(varName="o", bytecodeStart=112, bytecodeLength=1) + void m4() { + String o; + try (BufferedReader br = + new BufferedReader(new FileReader(o = "aFile"))) { + o = "inside try"; + o.hashCode(); + } catch (Exception e) {} + o = ""; + } +} diff -r d6d6e623f0b4 -r 8b91e8eb2d20 langtools/test/tools/javac/flow/tests/TestCaseWhile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/flow/tests/TestCaseWhile.java Sat Sep 14 19:04:47 2013 +0100 @@ -0,0 +1,15 @@ +/* /nodynamiccopyright/ */ + +public class TestCaseWhile { + + @AliveRange(varName="o", bytecodeStart=9, bytecodeLength=5) + @AliveRange(varName="o", bytecodeStart=20, bytecodeLength=1) + void m(String[] args) { + Object o; + while (args[0] != null) { + o = ""; + o.hashCode(); + } + o = ""; + } +}