/* * Copyright (c) 1994, 2003, 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 sun.tools.tree; import sun.tools.java.*; import sun.tools.asm.Assembler; /** * WARNING: The contents of this source file are not part of any * supported API. Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */ public class Context implements Constants { Context prev; Node node; int varNumber; LocalMember locals; LocalMember classes; MemberDefinition field; int scopeNumber; int frameNumber; /** * Create the initial context for a method * The incoming context is inherited from */ 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, // which will contain a "this" parameter. this.frameNumber = ctx.frameNumber; this.scopeNumber = ctx.scopeNumber + 1; } else { this.frameNumber = ctx.scopeNumber + 1; this.scopeNumber = this.frameNumber + 1; } this.varNumber = ctx.varNumber; } } /** * Create a new context, for initializing a class. */ public Context(Context ctx, ClassDefinition c) { this(ctx, (MemberDefinition)null); } /** * Create a new nested context, for a block statement */ 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, // just as for local variables. Fixes 4074421. 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); } /** * Declare local */ public int declare(Environment env, LocalMember local) { //System.out.println( "DECLARE= " + local.getName() + "=" + varNumber + ", read=" + local.readcount + ", write=" + local.writecount + ", hash=" + local.hashCode()); local.scopeNumber = scopeNumber; if (this.field == null && idThis.equals(local.getName())) { local.scopeNumber += 1; // Anticipate variable or initializer. } 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; } /** * Get a local variable by name */ public LocalMember getLocalField(Identifier name) { for (LocalMember f = locals ; f != null ; f = f.prev) { if (name.equals(f.getName())) { return f; } } return null; } /** * Get the scope number for a reference to a member of this class * (Larger scope numbers are more deeply nested.) * @see LocalMember#scopeNumber */ 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(); // Also look for a class member in a shallower scope. 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; } /** * Assign a number to a class field. * (This is used to track definite assignment of some blank finals.) */ public int declareFieldNumber(MemberDefinition field) { return declare(null, new LocalMember(field)); } /** * Retrieve a number previously assigned by declareMember(). * Return -1 if there was no such assignment in this context. */ public int getFieldNumber(MemberDefinition field) { for (LocalMember f = locals ; f != null ; f = f.prev) { if (f.getMember() == field) { return f.number; } } return -1; } /** * Return the local field or member field corresponding to a number. * Return null if there is no such field. */ 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; } /** * Get a local class by name */ 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; // Also look for a class member in a shallower scope. 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. // See 'ClassDefinition.getInnerClass(env, nm)'. MemberDefinition f = c.getInnerClass(env, name); if (f != null && getScopeNumber(c) > ls) { if (apparentOnly && f.getClassDefinition() != c) { continue; } return f; } } return lf; } /** * Get either a local variable, or a field in a current class */ public final MemberDefinition getField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound { return getFieldCommon(env, name, false); } /** * Like getField, except that it skips over inherited fields. * Used for error checking. */ public final MemberDefinition getApparentField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound { return getFieldCommon(env, name, true); } /** * Check if the given field is active in this context. */ public boolean isInScope(LocalMember field) { for (LocalMember f = locals ; f != null ; f = f.prev) { if (field == f) { return true; } } return false; } /** * Notice a reference (usually an uplevel one). * Update the references list of every enclosing class * which is enclosed by the scope of the target. * Update decisions about which uplevels to make into fields. * Return the uplevel reference descriptor, or null if it's local. *
* The target must be in scope in this context. * So, call this method only from the check phase. * (In other phases, the context may be less complete.) *
* This can and should be called both before and after classes are frozen. * It should be a no-op, and will raise a compiler error if not. */ 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, // record the event in the references list of the enclosing class. UplevelReference res = null; int currentFrameNumber = -1; for (Context refctx = this; refctx != null; refctx = refctx.prev) { if (currentFrameNumber == refctx.frameNumber) { continue; // we're processing frames, not contexts } currentFrameNumber = refctx.frameNumber; if (targetScopeNumber >= currentFrameNumber) { break; // the target is native to this frame } // process a frame which is using this variable as an uplevel ClassDefinition refc = refctx.field.getClassDefinition(); UplevelReference r = refc.getReference(target); r.noteReference(env, refctx); // remember the reference pertaining to the innermost frame if (res == null) { res = r; } } return res; } /** * Implement a reference (usually an uplevel one). * Call noteReference() first, to make sure the reference * lists are up to date. *
* The resulting expression tree does not need checking; * it can be code-generated right away. * If the reference is not uplevel, the result is an IDENT or THIS. */ public Expression makeReference(Environment env, LocalMember target) { UplevelReference r = noteReference(env, target); // Now create a referencing expression. 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); } } /** * Return a local expression which can serve as the base reference * for the given field. If the field is a constructor, return an * expression for the implicit enclosing instance argument. *
* Return null if there is no need for such an argument,
* or if there was an error.
*/
public Expression findOuterLink(Environment env, long where,
MemberDefinition f) {
// reqc is the base pointer type required to use 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) {
// say something like: undefined variable A.this
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
// the exception.
Expression e = new ThisExpression(where, this);
e.type = reqc.getType();
return e;
}
// use lp to scan for current instances (locals named "this")
LocalMember lp = locals;
// thise is a link expression being built up
Expression thise = null;
// root is the local variable (idThis) at the far left of thise
LocalMember root = null;
// thisc is the class of the link expression thise
ClassDefinition thisc = null;
// conCls is the class of the "this", in a constructor
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) {
// start fresh from lp
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
// the '