|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.tree; |
|
|
|
import sun.tools.java.*; |
|
import sun.tools.asm.Assembler; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
class Context implements Constants { |
|
Context prev; |
|
Node node; |
|
int varNumber; |
|
LocalMember locals; |
|
LocalMember classes; |
|
MemberDefinition field; |
|
int scopeNumber; |
|
int frameNumber; |
|
|
|
|
|
|
|
|
|
*/ |
|
public Context(Context ctx, MemberDefinition field) { |
|
this.field = field; |
|
if (ctx == null) { |
|
this.frameNumber = 1; |
|
this.scopeNumber = 2; |
|
this.varNumber = 0; |
|
} else { |
|
this.prev = ctx; |
|
this.locals = ctx.locals; |
|
this.classes = ctx.classes; |
|
if (field != null && |
|
(field.isVariable() || field.isInitializer())) { |
|
// Variables and initializers are inlined into a constructor. |
|
// Model this by inheriting the frame number of the parent, |
|
|
|
this.frameNumber = ctx.frameNumber; |
|
this.scopeNumber = ctx.scopeNumber + 1; |
|
} else { |
|
this.frameNumber = ctx.scopeNumber + 1; |
|
this.scopeNumber = this.frameNumber + 1; |
|
} |
|
this.varNumber = ctx.varNumber; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Context(Context ctx, ClassDefinition c) { |
|
this(ctx, (MemberDefinition)null); |
|
} |
|
|
|
|
|
|
|
*/ |
|
Context(Context ctx, Node node) { |
|
if (ctx == null) { |
|
this.frameNumber = 1; |
|
this.scopeNumber = 2; |
|
this.varNumber = 0; |
|
} else { |
|
this.prev = ctx; |
|
this.locals = ctx.locals; |
|
// Inherit local classes from surrounding block, |
|
|
|
this.classes = ctx.classes; |
|
this.varNumber = ctx.varNumber; |
|
this.field = ctx.field; |
|
this.frameNumber = ctx.frameNumber; |
|
this.scopeNumber = ctx.scopeNumber + 1; |
|
this.node = node; |
|
} |
|
} |
|
|
|
public Context(Context ctx) { |
|
this(ctx, (Node)null); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int declare(Environment env, LocalMember local) { |
|
|
|
local.scopeNumber = scopeNumber; |
|
if (this.field == null && idThis.equals(local.getName())) { |
|
local.scopeNumber += 1; |
|
} |
|
if (local.isInnerClass()) { |
|
local.prev = classes; |
|
classes = local; |
|
return 0; |
|
} |
|
|
|
// Originally the statement: |
|
// |
|
// local.subModifiers(M_INLINEABLE); |
|
// |
|
// was here with the comment: |
|
// |
|
// // prevent inlining across call sites |
|
// |
|
// This statement prevented constant local variables from |
|
// inlining. It didn't seem to do anything useful. |
|
// |
|
// The statement has been removed and an assertion has been |
|
// added which mandates that the only members which are marked |
|
// with M_INLINEABLE are the ones for which isConstant() is true. |
|
// (Fix for 4106244.) |
|
// |
|
// Addition to the above comment: they might also be |
|
// final variables initialized with 'this', 'super', or other |
|
// final identifiers. See VarDeclarationStatement.inline(). |
|
// So I've removed the assertion. The original subModifiers |
|
// call appears to have been there to fix nested class translation |
|
// breakage, which has been fixed in VarDeclarationStatement |
|
// now instead. (Fix for 4073244.) |
|
|
|
local.prev = locals; |
|
locals = local; |
|
local.number = varNumber; |
|
varNumber += local.getType().stackSize(); |
|
return local.number; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public |
|
LocalMember getLocalField(Identifier name) { |
|
for (LocalMember f = locals ; f != null ; f = f.prev) { |
|
if (name.equals(f.getName())) { |
|
return f; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
int getScopeNumber(ClassDefinition c) { |
|
for (Context ctx = this; ctx != null; ctx = ctx.prev) { |
|
if (ctx.field == null) continue; |
|
if (ctx.field.getClassDefinition() == c) { |
|
return ctx.frameNumber; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
private |
|
MemberDefinition getFieldCommon(Environment env, Identifier name, |
|
boolean apparentOnly) throws AmbiguousMember, ClassNotFound { |
|
// Note: This is structured as a pair of parallel lookups. |
|
// If we were to redesign Context, we might prefer to walk |
|
// along a single chain of scopes. |
|
|
|
LocalMember lf = getLocalField(name); |
|
int ls = (lf == null) ? -2 : lf.scopeNumber; |
|
|
|
ClassDefinition thisClass = field.getClassDefinition(); |
|
|
|
|
|
for (ClassDefinition c = thisClass; |
|
c != null; |
|
c = c.getOuterClass()) { |
|
MemberDefinition f = c.getVariable(env, name, thisClass); |
|
if (f != null && getScopeNumber(c) > ls) { |
|
if (apparentOnly && f.getClassDefinition() != c) { |
|
continue; |
|
} |
|
return f; |
|
} |
|
} |
|
|
|
return lf; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public int declareFieldNumber(MemberDefinition field) { |
|
return declare(null, new LocalMember(field)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public int getFieldNumber(MemberDefinition field) { |
|
for (LocalMember f = locals ; f != null ; f = f.prev) { |
|
if (f.getMember() == field) { |
|
return f.number; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getElement(int number) { |
|
for (LocalMember f = locals ; f != null ; f = f.prev) { |
|
if (f.number == number) { |
|
MemberDefinition field = f.getMember(); |
|
return (field != null) ? field : f; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public |
|
LocalMember getLocalClass(Identifier name) { |
|
for (LocalMember f = classes ; f != null ; f = f.prev) { |
|
if (name.equals(f.getName())) { |
|
return f; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
private |
|
MemberDefinition getClassCommon(Environment env, Identifier name, |
|
boolean apparentOnly) throws ClassNotFound { |
|
LocalMember lf = getLocalClass(name); |
|
int ls = (lf == null) ? -2 : lf.scopeNumber; |
|
|
|
|
|
for (ClassDefinition c = field.getClassDefinition(); |
|
c != null; |
|
c = c.getOuterClass()) { |
|
// QUERY: We may need to get the inner class from a |
|
// superclass of 'c'. This call is prepared to |
|
// resolve the superclass if necessary. Can we arrange |
|
// to assure that it is always previously resolved? |
|
// This is one of a small number of problematic calls that |
|
// requires 'getSuperClass' to resolve superclasses on demand. |
|
|
|
MemberDefinition f = c.getInnerClass(env, name); |
|
if (f != null && getScopeNumber(c) > ls) { |
|
if (apparentOnly && f.getClassDefinition() != c) { |
|
continue; |
|
} |
|
return f; |
|
} |
|
} |
|
|
|
return lf; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final |
|
MemberDefinition getField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound { |
|
return getFieldCommon(env, name, false); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final |
|
MemberDefinition getApparentField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound { |
|
return getFieldCommon(env, name, true); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean isInScope(LocalMember field) { |
|
for (LocalMember f = locals ; f != null ; f = f.prev) { |
|
if (field == f) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public UplevelReference noteReference(Environment env, LocalMember target) { |
|
int targetScopeNumber = !isInScope(target) ? -1 : target.scopeNumber; |
|
|
|
// Walk outward visiting each scope. |
|
// Note each distinct frame (i.e., enclosing method). |
|
// For each frame in which the variable is uplevel, |
|
|
|
UplevelReference res = null; |
|
int currentFrameNumber = -1; |
|
for (Context refctx = this; refctx != null; refctx = refctx.prev) { |
|
if (currentFrameNumber == refctx.frameNumber) { |
|
continue; |
|
} |
|
currentFrameNumber = refctx.frameNumber; |
|
if (targetScopeNumber >= currentFrameNumber) { |
|
break; |
|
} |
|
|
|
|
|
ClassDefinition refc = refctx.field.getClassDefinition(); |
|
UplevelReference r = refc.getReference(target); |
|
r.noteReference(env, refctx); |
|
|
|
|
|
if (res == null) { |
|
res = r; |
|
} |
|
} |
|
return res; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Expression makeReference(Environment env, LocalMember target) { |
|
UplevelReference r = noteReference(env, target); |
|
|
|
|
|
if (r != null) { |
|
return r.makeLocalReference(env, this); |
|
} else if (idThis.equals(target.getName())) { |
|
return new ThisExpression(0, target); |
|
} else { |
|
return new IdentifierExpression(0, target); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Expression findOuterLink(Environment env, long where, |
|
MemberDefinition f) { |
|
|
|
ClassDefinition fc = f.getClassDefinition(); |
|
ClassDefinition reqc = f.isStatic() ? null |
|
: !f.isConstructor() ? fc |
|
: fc.isTopLevel() ? null |
|
: fc.getOuterClass(); |
|
if (reqc == null) { |
|
return null; |
|
} |
|
return findOuterLink(env, where, reqc, f, false); |
|
} |
|
|
|
private static boolean match(Environment env, |
|
ClassDefinition thisc, ClassDefinition reqc) { |
|
try { |
|
return thisc == reqc |
|
|| reqc.implementedBy(env, thisc.getClassDeclaration()); |
|
} catch (ClassNotFound ee) { |
|
return false; |
|
} |
|
} |
|
|
|
public Expression findOuterLink(Environment env, long where, |
|
ClassDefinition reqc, |
|
MemberDefinition f, |
|
boolean needExactMatch) { |
|
if (field.isStatic()) { |
|
if (f == null) { |
|
|
|
Identifier nm = reqc.getName().getFlatName().getName(); |
|
env.error(where, "undef.var", Identifier.lookup(nm,idThis)); |
|
} else if (f.isConstructor()) { |
|
env.error(where, "no.outer.arg", reqc, f.getClassDeclaration()); |
|
} else if (f.isMethod()) { |
|
env.error(where, "no.static.meth.access", |
|
f, f.getClassDeclaration()); |
|
} else { |
|
env.error(where, "no.static.field.access", f.getName(), |
|
f.getClassDeclaration()); |
|
} |
|
// This is an attempt at error recovery. |
|
// Unfortunately, the constructor may throw |
|
// a null pointer exception after failing to resolve |
|
// 'idThis'. Since an error message has already been |
|
// issued previously, this exception is caught and |
|
// silently ignored. Ideally, we should avoid throwing |
|
|
|
Expression e = new ThisExpression(where, this); |
|
e.type = reqc.getType(); |
|
return e; |
|
} |
|
|
|
|
|
LocalMember lp = locals; |
|
|
|
|
|
Expression thise = null; |
|
|
|
|
|
LocalMember root = null; |
|
|
|
|
|
ClassDefinition thisc = null; |
|
|
|
|
|
ClassDefinition conCls = null; |
|
if (field.isConstructor()) { |
|
conCls = field.getClassDefinition(); |
|
} |
|
|
|
if (!field.isMethod()) { |
|
thisc = field.getClassDefinition(); |
|
thise = new ThisExpression(where, this); |
|
} |
|
|
|
while (true) { |
|
if (thise == null) { |
|
|
|
while (lp != null && !idThis.equals(lp.getName())) { |
|
lp = lp.prev; |
|
} |
|
if (lp == null) { |
|
break; |
|
} |
|
thise = new ThisExpression(where, lp); |
|
thisc = lp.getClassDefinition(); |
|
root = lp; |
|
lp = lp.prev; |
|
} |
|
|
|
// Require exact class identity when called with |
|
// 'needExactMatch' true. This is done when checking |
|
|
|
if (thisc == reqc || |
|
(!needExactMatch && match(env, thisc, reqc))) { |
|
break; |
|
} |
|
|
|
// move out one step, if the current instance has an outer link |
|
|
|
MemberDefinition outerMember = thisc.findOuterMember(); |
|
if (outerMember == null) { |
|
thise = null; |
|
continue; |
|
} |
|
ClassDefinition prevc = thisc; |
|
thisc = prevc.getOuterClass(); |
|
|
|
if (prevc == conCls) { |
|
// Must pick up "this$C" from the constructor argument, |
|
// not from "this.this$C", since the latter may not be |
|
|
|
Identifier nm = outerMember.getName(); |
|
IdentifierExpression arg = new IdentifierExpression(where, nm); |
|
arg.bind(env, this); |
|
thise = arg; |
|
} else { |
|
thise = new FieldExpression(where, thise, outerMember); |
|
} |
|
} |
|
if (thise != null) { |
|
// mark crossed scopes |
|
// ????? |
|
|
|
return thise; |
|
} |
|
|
|
if (f == null) { |
|
|
|
Identifier nm = reqc.getName().getFlatName().getName(); |
|
env.error(where, "undef.var", Identifier.lookup(nm,idThis)); |
|
} else if (f.isConstructor()) { |
|
env.error(where, "no.outer.arg", reqc, f.getClassDefinition()); |
|
} else { |
|
env.error(where, "no.static.field.access", f, field); |
|
} |
|
|
|
|
|
Expression e = new ThisExpression(where, this); |
|
e.type = reqc.getType(); |
|
return e; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static boolean outerLinkExists(Environment env, |
|
ClassDefinition reqc, |
|
ClassDefinition thisc) { |
|
while (!match(env, thisc, reqc)) { |
|
if (thisc.isTopLevel()) { |
|
return false; |
|
} |
|
thisc = thisc.getOuterClass(); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ClassDefinition findScope(Environment env, ClassDefinition reqc) { |
|
ClassDefinition thisc = field.getClassDefinition(); |
|
while (thisc != null && !match(env, thisc, reqc)) { |
|
thisc = thisc.getOuterClass(); |
|
} |
|
return thisc; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
Identifier resolveName(Environment env, Identifier name) { |
|
// This logic is pretty much exactly parallel to that of |
|
|
|
if (name.isQualified()) { |
|
// Try to resolve the first identifier component, |
|
// because inner class names take precedence over |
|
|
|
Identifier rhead = resolveName(env, name.getHead()); |
|
|
|
if (rhead.hasAmbigPrefix()) { |
|
// The first identifier component refers to an |
|
// ambiguous class. Limp on. We throw away the |
|
// rest of the classname as it is irrelevant. |
|
|
|
return rhead; |
|
} |
|
|
|
if (!env.classExists(rhead)) { |
|
return env.resolvePackageQualifiedName(name); |
|
} |
|
try { |
|
return env.getClassDefinition(rhead). |
|
resolveInnerClass(env, name.getTail()); |
|
} catch (ClassNotFound ee) { |
|
|
|
return Identifier.lookupInner(rhead, name.getTail()); |
|
} |
|
} |
|
|
|
|
|
try { |
|
MemberDefinition f = getClassCommon(env, name, false); |
|
if (f != null) { |
|
return f.getInnerClass().getName(); |
|
} |
|
} catch (ClassNotFound ee) { |
|
// a missing superclass, or something catastrophic |
|
} |
|
|
|
|
|
return env.resolveName(name); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
Identifier getApparentClassName(Environment env, Identifier name) { |
|
if (name.isQualified()) { |
|
// Try to resolve the first identifier component, |
|
// because inner class names take precedence over |
|
|
|
Identifier rhead = getApparentClassName(env, name.getHead()); |
|
return (rhead == null) ? idNull |
|
: Identifier.lookup(rhead, |
|
name.getTail()); |
|
} |
|
|
|
|
|
try { |
|
MemberDefinition f = getClassCommon(env, name, true); |
|
if (f != null) { |
|
return f.getInnerClass().getName(); |
|
} |
|
} catch (ClassNotFound ee) { |
|
// a missing superclass, or something catastrophic |
|
} |
|
|
|
|
|
Identifier topnm = field.getClassDefinition().getTopClass().getName(); |
|
if (topnm.getName().equals(name)) { |
|
return topnm; |
|
} |
|
return idNull; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void checkBackBranch(Environment env, Statement loop, |
|
Vset vsEntry, Vset vsBack) { |
|
for (LocalMember f = locals ; f != null ; f = f.prev) { |
|
if (f.isBlankFinal() |
|
&& vsEntry.testVarUnassigned(f.number) |
|
&& !vsBack.testVarUnassigned(f.number)) { |
|
env.error(loop.where, "assign.to.blank.final.in.loop", |
|
f.getName()); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean canReach(Environment env, MemberDefinition f) { |
|
return field.canReach(env, f); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
Context getLabelContext(Identifier lbl) { |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
if ((ctx.node != null) && (ctx.node instanceof Statement)) { |
|
if (((Statement)(ctx.node)).hasLabel(lbl)) |
|
return ctx; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public |
|
Context getBreakContext(Identifier lbl) { |
|
if (lbl != null) { |
|
return getLabelContext(lbl); |
|
} |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
if (ctx.node != null) { |
|
switch (ctx.node.op) { |
|
case SWITCH: |
|
case FOR: |
|
case DO: |
|
case WHILE: |
|
return ctx; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public |
|
Context getContinueContext(Identifier lbl) { |
|
if (lbl != null) { |
|
return getLabelContext(lbl); |
|
} |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
if (ctx.node != null) { |
|
switch (ctx.node.op) { |
|
case FOR: |
|
case DO: |
|
case WHILE: |
|
return ctx; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public |
|
CheckContext getReturnContext() { |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
|
|
if (ctx.node != null && ctx.node.op == METHOD) { |
|
return (CheckContext)ctx; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
CheckContext getTryExitContext() { |
|
for (Context ctx = this; |
|
ctx != null && ctx.node != null && ctx.node.op != METHOD; |
|
ctx = ctx.prev) { |
|
if (ctx.node.op == TRY) { |
|
return (CheckContext)ctx; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
Context getInlineContext() { |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
if (ctx.node != null) { |
|
switch (ctx.node.op) { |
|
case INLINEMETHOD: |
|
case INLINENEWINSTANCE: |
|
return ctx; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
Context getInlineMemberContext(MemberDefinition field) { |
|
for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { |
|
if (ctx.node != null) { |
|
switch (ctx.node.op) { |
|
case INLINEMETHOD: |
|
if (((InlineMethodExpression)ctx.node).field.equals(field)) { |
|
return ctx; |
|
} |
|
break; |
|
case INLINENEWINSTANCE: |
|
if (((InlineNewInstanceExpression)ctx.node).field.equals(field)) { |
|
return ctx; |
|
} |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final Vset removeAdditionalVars(Vset vset) { |
|
return vset.removeAdditionalVars(varNumber); |
|
} |
|
|
|
public final int getVarNumber() { |
|
return varNumber; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getThisNumber() { |
|
LocalMember thisf = getLocalField(idThis); |
|
if (thisf != null |
|
&& thisf.getClassDefinition() == field.getClassDefinition()) { |
|
return thisf.number; |
|
} |
|
|
|
return varNumber; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final MemberDefinition getField() { |
|
return field; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Environment newEnvironment(Environment env, Context ctx) { |
|
return new ContextEnvironment(env, ctx); |
|
} |
|
} |
|
|
|
final |
|
class ContextEnvironment extends Environment { |
|
Context ctx; |
|
Environment innerEnv; |
|
|
|
ContextEnvironment(Environment env, Context ctx) { |
|
super(env, env.getSource()); |
|
this.ctx = ctx; |
|
this.innerEnv = env; |
|
} |
|
|
|
public Identifier resolveName(Identifier name) { |
|
return ctx.resolveName(innerEnv, name); |
|
} |
|
} |