7047734: javac, the LVT is not generated correctly in several scenarios
Reviewed-by: jjg, mcimadamore
This commit is contained in:
parent
0a312ba2ce
commit
86baa378e4
@ -33,9 +33,6 @@ import com.sun.tools.javac.util.List;
|
||||
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 @@ public class Lint
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
private final AugmentVisitor augmentor;
|
||||
|
||||
private final EnumSet<LintCategory> values;
|
||||
@ -90,7 +86,6 @@ public class Lint
|
||||
private static final Map<String, LintCategory> map =
|
||||
new java.util.concurrent.ConcurrentHashMap<String, LintCategory>(20);
|
||||
|
||||
|
||||
protected Lint(Context context) {
|
||||
// initialize values according to the lint options
|
||||
Options options = Options.instance(context);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1745,6 +1745,11 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
// 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) {
|
||||
|
@ -1479,7 +1479,12 @@ public class Lower extends TreeTranslator {
|
||||
* @param owner The class in which the definitions go.
|
||||
*/
|
||||
List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner) {
|
||||
long flags = FINAL | SYNTHETIC;
|
||||
return freevarDefs(pos, freevars, owner, 0);
|
||||
}
|
||||
|
||||
List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner,
|
||||
long additionalFlags) {
|
||||
long flags = FINAL | SYNTHETIC | additionalFlags;
|
||||
if (owner.kind == TYP &&
|
||||
target.usePrivateSyntheticFields())
|
||||
flags |= PRIVATE;
|
||||
@ -1542,7 +1547,7 @@ public class Lower extends TreeTranslator {
|
||||
(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 @@ public class Lower extends TreeTranslator {
|
||||
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 @@ public class Lower extends TreeTranslator {
|
||||
return result;
|
||||
}
|
||||
|
||||
private JCBlock makeTwrBlock(List<JCTree> resources, JCBlock block, int depth) {
|
||||
private JCBlock makeTwrBlock(List<JCTree> resources, JCBlock block,
|
||||
boolean finallyCanCompleteNormally, int depth) {
|
||||
if (resources.isEmpty())
|
||||
return block;
|
||||
|
||||
@ -1691,17 +1698,20 @@ public class Lower extends TreeTranslator {
|
||||
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.<JCCatch>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 @@ public class Lower extends TreeTranslator {
|
||||
JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(addSuppressionStatement));
|
||||
List<JCCatch> catchClauses = List.<JCCatch>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 @@ public class Lower extends TreeTranslator {
|
||||
|
||||
// catchParam := ClassNotFoundException e1
|
||||
VarSymbol catchParam =
|
||||
new VarSymbol(0, make.paramName(1),
|
||||
new VarSymbol(SYNTHETIC, make.paramName(1),
|
||||
syms.classNotFoundExceptionType,
|
||||
classDollarSym);
|
||||
|
||||
@ -2704,7 +2715,7 @@ public class Lower extends TreeTranslator {
|
||||
JCVariableDecl otdef = null;
|
||||
if (currentClass.hasOuterInstance())
|
||||
otdef = outerThisDef(tree.pos, m);
|
||||
List<JCVariableDecl> fvdefs = freevarDefs(tree.pos, fvs, m);
|
||||
List<JCVariableDecl> fvdefs = freevarDefs(tree.pos, fvs, m, PARAMETER);
|
||||
|
||||
// Recursively translate result type, parameters and thrown list.
|
||||
tree.restype = translate(tree.restype);
|
||||
@ -3363,18 +3374,18 @@ public class Lower extends TreeTranslator {
|
||||
*/
|
||||
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 @@ public class Lower extends TreeTranslator {
|
||||
names.iterator,
|
||||
eType,
|
||||
List.<Type>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);
|
||||
|
||||
|
@ -1532,7 +1532,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
* 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 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
initParams = (initParams == null) ? List.<VarSymbol>nil() : initParams;
|
||||
List<VarSymbol> 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;
|
||||
|
@ -310,7 +310,7 @@ public class TransTypes extends TreeTranslator {
|
||||
Type.MethodType mType = (Type.MethodType)bridgeType;
|
||||
List<Type> 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);
|
||||
|
@ -37,7 +37,6 @@ import javax.tools.JavaFileObject;
|
||||
|
||||
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.jvm.UninitializedType.*;
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
|
||||
if (code.varBufferSize > 0) {
|
||||
int alenIdx = writeAttr(names.LocalVariableTable);
|
||||
databuf.appendChar(code.varBufferSize);
|
||||
|
||||
databuf.appendChar(code.getLVTSize());
|
||||
for (int i=0; i<code.varBufferSize; i++) {
|
||||
Code.LocalVar var = code.varBuffer[i];
|
||||
|
||||
// write variable info
|
||||
Assert.check(var.start_pc >= 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 @@ public class ClassWriter extends ClassFile {
|
||||
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);
|
||||
|
@ -28,6 +28,7 @@ package com.sun.tools.javac.jvm;
|
||||
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 @@ public class Code {
|
||||
|
||||
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 @@ public class Code {
|
||||
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 @@ public class Code {
|
||||
state = new State();
|
||||
lvar = new LocalVar[20];
|
||||
this.pool = pool;
|
||||
this.lvtRanges = lvtRanges;
|
||||
}
|
||||
|
||||
|
||||
@ -305,9 +310,19 @@ public class Code {
|
||||
|
||||
/** 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 @@ public class Code {
|
||||
/** 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 @@ public class Code {
|
||||
* 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 @@ public class Code {
|
||||
* 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 @@ public class Code {
|
||||
|
||||
/** Emit a stack map entry. */
|
||||
public void emitStackMap() {
|
||||
int pc = curPc();
|
||||
int pc = curCP();
|
||||
if (!needStackMap) return;
|
||||
|
||||
|
||||
@ -1482,6 +1497,9 @@ public class Code {
|
||||
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 @@ public class Code {
|
||||
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 @@ public class Code {
|
||||
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<Range> aliveRanges = new java.util.ArrayList<>();
|
||||
|
||||
LocalVar(VarSymbol v) {
|
||||
this.sym = v;
|
||||
this.reg = (char)v.adr;
|
||||
@ -1879,9 +1924,78 @@ public class Code {
|
||||
public LocalVar dup() {
|
||||
return new LocalVar(sym);
|
||||
}
|
||||
public String toString() {
|
||||
return "" + sym + " in register " + ((int)reg) + " starts at pc=" + ((int)start_pc) + " length=" + ((int)length);
|
||||
|
||||
Range firstRange() {
|
||||
return aliveRanges.isEmpty() ? null : aliveRanges.get(0);
|
||||
}
|
||||
|
||||
Range lastRange() {
|
||||
return aliveRanges.isEmpty() ? null : aliveRanges.get(aliveRanges.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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 @@ public class Code {
|
||||
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<VarSymbol> 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 @@ public class Code {
|
||||
} 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 @@ public class Code {
|
||||
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 @@ public class Code {
|
||||
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 @@ public class Code {
|
||||
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;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
package com.sun.tools.javac.jvm;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.sun.tools.javac.util.*;
|
||||
@ -95,10 +96,14 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
*/
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 class Gen extends JCTree.Visitor {
|
||||
*/
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
// 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 @@ public class Gen extends JCTree.Visitor {
|
||||
code.crt.put(tree.body,
|
||||
CRT_BLOCK,
|
||||
startpcCrt,
|
||||
code.curPc());
|
||||
code.curCP());
|
||||
|
||||
code.endScopes(0);
|
||||
|
||||
@ -1087,10 +1094,12 @@ public class Gen extends JCTree.Visitor {
|
||||
: 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 @@ public class Gen extends JCTree.Visitor {
|
||||
}
|
||||
|
||||
// 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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
?
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
|
||||
// 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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
*/
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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 @@ public class Gen extends JCTree.Visitor {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -789,18 +789,18 @@ public class Items {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* <p><b>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.</b>
|
||||
*/
|
||||
public class LVTRanges {
|
||||
/** The context key for the LVT ranges. */
|
||||
protected static final Context.Key<LVTRanges> 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<MethodSymbol, Map<JCTree, List<VarSymbol>>>
|
||||
aliveRangeClosingTrees = new WeakHashMap<>();
|
||||
|
||||
public LVTRanges(Context context) {
|
||||
this.context = context;
|
||||
context.put(lvtRangesKey, this);
|
||||
}
|
||||
|
||||
public List<VarSymbol> getVars(MethodSymbol method, JCTree tree) {
|
||||
Map<JCTree, List<VarSymbol>> varMap = aliveRangeClosingTrees.get(method);
|
||||
return (varMap != null) ? varMap.get(tree) : null;
|
||||
}
|
||||
|
||||
public boolean containsKey(MethodSymbol method, JCTree tree) {
|
||||
Map<JCTree, List<VarSymbol>> varMap = aliveRangeClosingTrees.get(method);
|
||||
if (varMap == null) {
|
||||
return false;
|
||||
}
|
||||
return varMap.containsKey(tree);
|
||||
}
|
||||
|
||||
public void setEntry(MethodSymbol method, JCTree tree, List<VarSymbol> vars) {
|
||||
Map<JCTree, List<VarSymbol>> 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<VarSymbol> removeEntry(MethodSymbol method, JCTree tree) {
|
||||
Map<JCTree, List<VarSymbol>> varMap = aliveRangeClosingTrees.get(method);
|
||||
if (varMap != null) {
|
||||
List<VarSymbol> 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<MethodSymbol, Map<JCTree, List<VarSymbol>>> mainEntry: aliveRangeClosingTrees.entrySet()) {
|
||||
result += "Method: \n" + mainEntry.getKey().flatName() + "\n";
|
||||
int i = 1;
|
||||
for (Entry<JCTree, List<VarSymbol>> 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;
|
||||
}
|
||||
|
||||
}
|
@ -890,7 +890,7 @@ public class TreeMaker implements JCTree.Factory {
|
||||
/** 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
|
||||
|
@ -27,8 +27,6 @@ package com.sun.tools.javac.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.sun.tools.javac.util.Bits.BitsOpKind.*;
|
||||
|
||||
/** A class for extensible, mutable bit sets.
|
||||
*
|
||||
* <p><b>This is NOT part of any supported API.
|
||||
@ -38,20 +36,6 @@ import static com.sun.tools.javac.util.Bits.BitsOpKind.*;
|
||||
*/
|
||||
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 @@ public class Bits {
|
||||
// | |
|
||||
// -----------
|
||||
// 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 class Bits {
|
||||
|
||||
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 @@ public class Bits {
|
||||
|
||||
/** 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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
}
|
||||
|
||||
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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
*/
|
||||
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 @@ public class Bits {
|
||||
*/
|
||||
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 class Bits {
|
||||
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 @@ public class Bits {
|
||||
System.out.println("found " + i);
|
||||
count ++;
|
||||
}
|
||||
if (count != 125) throw new Error();
|
||||
if (count != 125) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
langtools/test/tools/javac/flow/AliveRanges.java
Normal file
34
langtools/test/tools/javac/flow/AliveRanges.java
Normal file
@ -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();}
|
280
langtools/test/tools/javac/flow/LVTHarness.java
Normal file
280
langtools/test/tools/javac/flow/LVTHarness.java
Normal file
@ -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<ElementKey, AliveRanges> aliveRangeMap =
|
||||
new HashMap<ElementKey, AliveRanges>();
|
||||
Set<String> declaredKeys = new HashSet<>();
|
||||
List<ElementKey> 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<ElementKey, AliveRanges> 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<String> infoFromRanges = convertToStringList(ranges);
|
||||
List<String> 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<String> convertToStringList(AliveRanges ranges) {
|
||||
List<String> 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<String> convertToStringList(ConstantPool constantPool,
|
||||
LocalVariableTable_attribute lvt) throws InvalidIndex, UnexpectedEntry {
|
||||
List<String> 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<String> infoFromLVT, List<String> 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<String> 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<? extends TypeElement> 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 + "}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
15
langtools/test/tools/javac/flow/tests/TestCaseDoLoop.java
Normal file
15
langtools/test/tools/javac/flow/tests/TestCaseDoLoop.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
27
langtools/test/tools/javac/flow/tests/TestCaseFor.java
Normal file
27
langtools/test/tools/javac/flow/tests/TestCaseFor.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
15
langtools/test/tools/javac/flow/tests/TestCaseForEach.java
Normal file
15
langtools/test/tools/javac/flow/tests/TestCaseForEach.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
61
langtools/test/tools/javac/flow/tests/TestCaseIf.java
Normal file
61
langtools/test/tools/javac/flow/tests/TestCaseIf.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
48
langtools/test/tools/javac/flow/tests/TestCaseIfElse.java
Normal file
48
langtools/test/tools/javac/flow/tests/TestCaseIfElse.java
Normal file
@ -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";
|
||||
}
|
||||
}
|
73
langtools/test/tools/javac/flow/tests/TestCaseSwitch.java
Normal file
73
langtools/test/tools/javac/flow/tests/TestCaseSwitch.java
Normal file
@ -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";
|
||||
}
|
||||
}
|
76
langtools/test/tools/javac/flow/tests/TestCaseTry.java
Normal file
76
langtools/test/tools/javac/flow/tests/TestCaseTry.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
15
langtools/test/tools/javac/flow/tests/TestCaseWhile.java
Normal file
15
langtools/test/tools/javac/flow/tests/TestCaseWhile.java
Normal file
@ -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 = "";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user