--- 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<GenContext> 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<GenContext> 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<JCCase> 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<GenContext> 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<JCCatch> catchers, Env<GenContext> 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<JCCatch> 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<GenContext>(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<JCTree> 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<LVTAssignAnalyzer.LVTAssignPendingExit> {
+
+ 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<VarSymbol> locals =
+ analyzer.lvtRanges
+ .getVars(analyzer.currentMethod, currentTree);
+ locals = locals != null ?
+ locals : List.<VarSymbol>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);
+ }
+
+ }
+
}