7047734: javac, the LVT is not generated correctly in several scenarios

Reviewed-by: jjg, mcimadamore
This commit is contained in:
Vicente Romero 2013-09-14 19:04:47 +01:00
parent 0a312ba2ce
commit 86baa378e4
24 changed files with 1888 additions and 378 deletions

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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();
}
}
}

View 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();}

View 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 + "}";
}
}
}

View File

@ -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();
}
}

View 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 = "";
}
}

View 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 = "";
}
}

View 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 = "";
}
}

View 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 = "";
}
}

View 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";
}
}

View 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";
}
}

View 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 = "";
}
}

View 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 = "";
}
}