/*
* Copyright (c) 1994, 2004, 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.javac;
import sun.tools.java.*;
import sun.tools.tree.*;
import sun.tools.asm.*;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.io.PrintStream;
/**
* A Source Member
*
* 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.
*/
@Deprecated
public
class SourceMember extends MemberDefinition implements Constants {
/**
* The argument names (if it is a method)
*/
Vector args;
// set to the MemberDefinition in the interface if we have this field because
// it has been forced on us
MemberDefinition abstractSource;
/**
* The status of the field
*/
int status;
static final int PARSED = 0;
static final int CHECKING = 1;
static final int CHECKED = 2;
static final int INLINING = 3;
static final int INLINED = 4;
static final int ERROR = 5;
public Vector getArguments() {
return args;
}
/**
* Constructor
* @param argNames a vector of IdentifierToken
*/
public SourceMember(long where, ClassDefinition clazz,
String doc, int modifiers, Type type,
Identifier name, Vector argNames,
IdentifierToken exp[], Node value) {
super(where, clazz, modifiers, type, name, exp, value);
this.documentation = doc;
this.args = argNames; // for the moment
// not until type names are resolved: createArgumentFields(argNames);
if (ClassDefinition.containsDeprecated(documentation)) {
this.modifiers |= M_DEPRECATED;
}
}
void createArgumentFields(Vector argNames) {
// Create a list of arguments
if (isMethod()) {
args = new Vector();
if (isConstructor() || !(isStatic() || isInitializer())) {
args.addElement(((SourceClass)clazz).getThisArgument());
}
if (argNames != null) {
Enumeration e = argNames.elements();
Type argTypes[] = getType().getArgumentTypes();
for (int i = 0 ; i < argTypes.length ; i++) {
Object x = e.nextElement();
if (x instanceof LocalMember) {
// This should not happen, but it does
// in cases of vicious cyclic inheritance.
args = argNames;
return;
}
Identifier id;
int mod;
long where;
if (x instanceof Identifier) {
// allow argNames to be simple Identifiers (deprecated!)
id = (Identifier)x;
mod = 0;
where = getWhere();
} else {
IdentifierToken token = (IdentifierToken)x;
id = token.getName();
mod = token.getModifiers();
where = token.getWhere();
}
args.addElement(new LocalMember(where, clazz, mod,
argTypes[i], id));
}
}
}
}
// The methods addOuterThis() and addUplevelArguments() were
// both originally part of a single method called addUplevelArguments()
// which took a single boolean parameter describing which of the
// two behaviors it wanted.
//
// The original addUplevelArguments() claimed to keep the arguments in
// the following order:
//
// (1) <this> <early outer this> <uplevel arguments...> <true arguments...>
//
// (By <early outer this> I am referring to the clientOuterField added
// to some constructors when they are created. If an outer this is
// added later, on demand, then this is mixed in with the rest of the
// uplevel arguments and is added by addUplevelArguments.)
//
// In reality, the `args' Vector was generated in this order, but the
// Type array `argTypes' was generated as:
//
// (2) <this> <uplevel arguments...> <early outer this> <true arguments...>
//
// This didn't make a difference in the common case -- that is, when
// a class had an <outer.this> or <uplevel arguments...> but not both.
// Both can happen in the case that a member class is declared inside
// of a local class. It seems that the calling sequences, generated
// in places like NewInstanceExpression.codeCommon(), use order (2),
// so I have changed the code below to stick with that order. Since
// the only time this happens is in classes which are insideLocal, no
// one should be able to tell the difference between these orders.
// (bug number 4085633)
LocalMember outerThisArg = null;
/**
* Get outer instance link, or null if none.
*/
public LocalMember getOuterThisArg() {
return outerThisArg;
}
/**
* Add the outer.this argument to the list of arguments for this
* constructor. This is called from resolveTypeStructure. Any
* additional uplevel arguments get added later by addUplevelArguments().
*/
void addOuterThis() {
UplevelReference refs = clazz.getReferences();
// See if we have a client outer field.
while (refs != null &&
!refs.isClientOuterField()) {
refs = refs.getNext();
}
// There is no outer this argument. Quit.
if (refs == null) {
return;
}
// Get the old arg types.
Type oldArgTypes[] = type.getArgumentTypes();
// And make an array for the new ones with space for one more.
Type argTypes[] = new Type[oldArgTypes.length + 1];
LocalMember arg = refs.getLocalArgument();
outerThisArg = arg;
// args is our list of arguments. It contains a `this', so
// we insert at position 1. The list of types does not have a
// this, so we insert at position 0.
args.insertElementAt(arg, 1);
argTypes[0] = arg.getType();
// Add on the rest of the constructor arguments.
for (int i = 0; i < oldArgTypes.length; i++) {
argTypes[i + 1] = oldArgTypes[i];
}
type = Type.tMethod(type.getReturnType(), argTypes);
}
/**
* Prepend argument names and argument types for local variable references.
* This information is never seen by the type-check phase,
* but it affects code generation, which is the earliest moment
* we have comprehensive information on uplevel references.
* The code() methods tweaks the constructor calls, prepending
* the proper values to the argument list.
*/
void addUplevelArguments() {
UplevelReference refs = clazz.getReferences();
clazz.getReferencesFrozen();
// Count how many uplevels we have to add.
int count = 0;
for (UplevelReference r = refs; r != null; r = r.getNext()) {
if (!r.isClientOuterField()) {
count += 1;
}
}
if (count == 0) {
// None to add, quit.
return;
}
// Get the old argument types.
Type oldArgTypes[] = type.getArgumentTypes();
// Make an array with enough room for the new.
Type argTypes[] = new Type[oldArgTypes.length + count];
// Add all of the late uplevel references to args and argTypes.
// Note that they are `off-by-one' because of the `this'.
int ins = 0;
for (UplevelReference r = refs; r != null; r = r.getNext()) {
if (!r.isClientOuterField()) {
LocalMember arg = r.getLocalArgument();
args.insertElementAt(arg, 1 + ins);
argTypes[ins] = arg.getType();
ins++;
}
}
// Add the rest of the old arguments.
for (int i = 0; i < oldArgTypes.length; i++) {
argTypes[ins + i] = oldArgTypes[i];
}
type = Type.tMethod(type.getReturnType(), argTypes);
}
/**
* Constructor for an inner class.
*/
public SourceMember(ClassDefinition innerClass) {
super(innerClass);
}
/**
* Constructor.
* Used only to generate an abstract copy of a method that a class
* inherits from an interface
*/
public SourceMember(MemberDefinition f, ClassDefinition c, Environment env) {
this(f.getWhere(), c, f.getDocumentation(),
f.getModifiers() | M_ABSTRACT, f.getType(), f.getName(), null,
f.getExceptionIds(), null);
this.args = f.getArguments();
this.abstractSource = f;
this.exp = f.getExceptions(env);
}
/**
* Get exceptions
*/
public ClassDeclaration[] getExceptions(Environment env) {
if ((!isMethod()) || (exp != null)) {
return exp;
}
if (expIds == null) {
// (should not happen)
exp = new ClassDeclaration[0];
return exp;
}
// be sure to get the imports right:
env = ((SourceClass)getClassDefinition()).setupEnv(env);
exp = new ClassDeclaration[expIds.length];
for (int i = 0; i < exp.length; i++) {
Identifier e = expIds[i].getName();
Identifier rexp = getClassDefinition().resolveName(env, e);
exp[i] = env.getClassDeclaration(rexp);
}
return exp;
}
/**
* Set array of name-resolved exceptions directly, e.g., for access methods.
*/
public void setExceptions(ClassDeclaration[] exp) {
this.exp = exp;
}
/**
* Resolve types in a field, after parsing.
* @see ClassDefinition.resolveTypeStructure
*/
public boolean resolved = false;
public void resolveTypeStructure(Environment env) {
if (tracing) env.dtEnter("SourceMember.resolveTypeStructure: " + this);
// A member should only be resolved once. For a constructor, it is imperative
// that 'addOuterThis' be called only once, else the outer instance argument may
// be inserted into the argument list multiple times.
if (resolved) {
if (tracing) env.dtEvent("SourceMember.resolveTypeStructure: OK " + this);
// This case shouldn't be happening. It is the responsibility
// of our callers to avoid attempting multiple resolutions of a member.
// *** REMOVE FOR SHIPMENT? ***
throw new CompilerError("multiple member type resolution");
//return;
} else {
if (tracing) env.dtEvent("SourceMember.resolveTypeStructure: RESOLVING " + this);
resolved = true;
}
super.resolveTypeStructure(env);
if (isInnerClass()) {
ClassDefinition nc = getInnerClass();
if (nc instanceof SourceClass && !nc.isLocal()) {
((SourceClass)nc).resolveTypeStructure(env);
}
type = innerClass.getType();
} else {
// Expand all class names in 'type', including those that are not
// fully-qualified or refer to inner classes, into fully-qualified
// names. Local and anonymous classes get synthesized names here,
// corresponding to the class files that will be generated. This is
// currently the only place where 'resolveNames' is used.
type = env.resolveNames(getClassDefinition(), type, isSynthetic());
// do the throws also:
getExceptions(env);
if (isMethod()) {
Vector argNames = args; args = null;
createArgumentFields(argNames);
// Add outer instance argument for constructors.
if (isConstructor()) {
addOuterThis();
}
}
}
if (tracing) env.dtExit("SourceMember.resolveTypeStructure: " + this);
}
/**
* Get the class declaration in which the field is actually defined
*/
public ClassDeclaration getDefiningClassDeclaration() {
if (abstractSource == null)
return super.getDefiningClassDeclaration();
else
return abstractSource.getDefiningClassDeclaration();
}
/**
* A source field never reports deprecation, since the compiler
* allows access to deprecated features that are being compiled
* in the same job.
*/
public boolean reportDeprecated(Environment env) {
return false;
}
/**
* Check this field.
* <p>
* This is the method which requests checking.
* The real work is done by
* <tt>Vset check(Environment, Context, Vset)</tt>.
*/
public void check(Environment env) throws ClassNotFound {
if (tracing) env.dtEnter("SourceMember.check: " +
getName() + ", status = " + status);
// rely on the class to check all fields in the proper order
if (status == PARSED) {
if (isSynthetic() && getValue() == null) {
// break a big cycle for small synthetic variables
status = CHECKED;
if (tracing)
env.dtExit("SourceMember.check: BREAKING CYCLE");
return;
}
if (tracing) env.dtEvent("SourceMember.check: CHECKING CLASS");
clazz.check(env);
if (status == PARSED) {
if (getClassDefinition().getError()) {
status = ERROR;
} else {
if (tracing)
env.dtExit("SourceMember.check: CHECK FAILED");
throw new CompilerError("check failed");
}
}
}
if (tracing) env.dtExit("SourceMember.check: DONE " +
getName() + ", status = " + status);
}
/**
* Check a field.
* @param vset tells which uplevel variables are definitely assigned
* The vset is also used to track the initialization of blank finals
* by whichever fields which are relevant to them.
*/
public Vset check(Environment env, Context ctx, Vset vset) throws ClassNotFound {
if (tracing) env.dtEvent("SourceMember.check: MEMBER " +
getName() + ", status = " + status);
if (status == PARSED) {
if (isInnerClass()) {
// some classes are checked separately
ClassDefinition nc = getInnerClass();
if (nc instanceof SourceClass && !nc.isLocal()
&& nc.isInsideLocal()) {
status = CHECKING;
vset = ((SourceClass)nc).checkInsideClass(env, ctx, vset);
}
status = CHECKED;
return vset;
}
if (env.dump()) {
System.out.println("[check field " + getClassDeclaration().getName() + "." + getName() + "]");
if (getValue() != null) {
getValue().print(System.out);
System.out.println();
}
}
env = new Environment(env, this);
// This is where all checking of names appearing within the type
// of the member is done. Includes return type and argument types.
// Since only one location ('where') for error messages is provided,
// localization of errors is poor. Throws clauses are handled below.
env.resolve(where, getClassDefinition(), getType());
// Make sure that all the classes that we claim to throw really
// are subclasses of Throwable, and are classes that we can reach
if (isMethod()) {
ClassDeclaration throwable =
env.getClassDeclaration(idJavaLangThrowable);
ClassDeclaration exp[] = getExceptions(env);
for (int i = 0 ; i < exp.length ; i++) {
ClassDefinition def;
long where = getWhere();
if (expIds != null && i < expIds.length) {
where = IdentifierToken.getWhere(expIds[i], where);
}
try {
def = exp[i].getClassDefinition(env);
// Validate access for all inner-class components
// of a qualified name, not just the last one, which
// is checked below. Yes, this is a dirty hack...
// Part of fix for 4094658.
env.resolveByName(where, getClassDefinition(), def.getName());
} catch (ClassNotFound e) {
env.error(where, "class.not.found", e.name, "throws");
break;
}
def.noteUsedBy(getClassDefinition(), where, env);
if (!getClassDefinition().
canAccess(env, def.getClassDeclaration())) {
env.error(where, "cant.access.class", def);
} else if (!def.subClassOf(env, throwable)) {
env.error(where, "throws.not.throwable", def);
}
}
}
status = CHECKING;
if (isMethod() && args != null) {
int length = args.size();
outer_loop:
for (int i = 0; i < length; i++) {
LocalMember lf = (LocalMember)(args.elementAt(i));
Identifier name_i = lf.getName();
for (int j = i + 1; j < length; j++) {
LocalMember lf2 = (LocalMember)(args.elementAt(j));
Identifier name_j = lf2.getName();
if (name_i.equals(name_j)) {
env.error(lf2.getWhere(), "duplicate.argument",
name_i);
break outer_loop;
}
}
}
}
if (getValue() != null) {
ctx = new Context(ctx, this);
if (isMethod()) {
Statement s = (Statement)getValue();
// initialize vset, indication that each of the arguments
// to the function has a value
for (Enumeration e = args.elements(); e.hasMoreElements();){
LocalMember f = (LocalMember)e.nextElement();
vset.addVar(ctx.declare(env, f));
}
if (isConstructor()) {
// Undefine "this" in some constructors, until after
// the super constructor has been called.
vset.clearVar(ctx.getThisNumber());
// If the first thing in the definition isn't a call
// to either super() or this(), then insert one.
Expression supCall = s.firstConstructor();
if ((supCall == null)
&& (getClassDefinition().getSuperClass() != null)) {
supCall = getDefaultSuperCall(env);
Statement scs = new ExpressionStatement(where,
supCall);
s = Statement.insertStatement(scs, s);
setValue(s);
}
}
//System.out.println("VSET = " + vset);
ClassDeclaration exp[] = getExceptions(env);
int htsize = (exp.length > 3) ? 17 : 7;
Hashtable thrown = new Hashtable(htsize);
vset = s.checkMethod(env, ctx, vset, thrown);
ClassDeclaration ignore1 =
env.getClassDeclaration(idJavaLangError);
ClassDeclaration ignore2 =
env.getClassDeclaration(idJavaLangRuntimeException);
for (Enumeration e = thrown.keys(); e.hasMoreElements();) {
ClassDeclaration c = (ClassDeclaration)e.nextElement();
ClassDefinition def = c.getClassDefinition(env);
if (def.subClassOf(env, ignore1)
|| def.subClassOf(env, ignore2)) {
continue;
}
boolean ok = false;
if (!isInitializer()) {
for (int i = 0 ; i < exp.length ; i++) {
if (def.subClassOf(env, exp[i])) {
ok = true;
}
}
}
if (!ok) {
Node n = (Node)thrown.get(c);
long where = n.getWhere();
String errorMsg;
if (isConstructor()) {
if (where ==
getClassDefinition().getWhere()) {
// If this message is being generated for
// a default constructor, we should give
// a different error message. Currently
// we check for this by seeing if the
// constructor has the same "where" as
// its class. This is a bit kludgy, but
// works. (bug id 4034836)
errorMsg = "def.constructor.exception";
} else {
// Constructor with uncaught exception.
errorMsg = "constructor.exception";
}
} else if (isInitializer()) {
// Initializer with uncaught exception.
errorMsg = "initializer.exception";
} else {
// Method with uncaught exception.
errorMsg = "uncaught.exception";
}
env.error(where, errorMsg, c.getName());
}
}
} else {
Hashtable thrown = new Hashtable(3); // small & throw-away
Expression val = (Expression)getValue();
vset = val.checkInitializer(env, ctx, vset,
getType(), thrown);
setValue(val.convert(env, ctx, getType(), val));
// Complain about static final members of inner classes that
// do not have an initializer that is a constant expression.
// In general, static members are not permitted for inner
// classes, but an exception is made for named constants.
// Other cases of static members, including non-final ones,
// are handled in 'SourceClass'. Part of fix for 4095568.
if (isStatic() && isFinal() && !clazz.isTopLevel()) {
if (!((Expression)getValue()).isConstant()) {
env.error(where, "static.inner.field", getName(), this);
setValue(null);
}
}
// Both RuntimeExceptions and Errors should be
// allowed in initializers. Fix for bug 4102541.
ClassDeclaration except =
env.getClassDeclaration(idJavaLangThrowable);
ClassDeclaration ignore1 =
env.getClassDeclaration(idJavaLangError);
ClassDeclaration ignore2 =
env.getClassDeclaration(idJavaLangRuntimeException);
for (Enumeration e = thrown.keys(); e.hasMoreElements(); ) {
ClassDeclaration c = (ClassDeclaration)e.nextElement();
ClassDefinition def = c.getClassDefinition(env);
if (!def.subClassOf(env, ignore1)
&& !def.subClassOf(env, ignore2)
&& def.subClassOf(env, except)) {
Node n = (Node)thrown.get(c);
env.error(n.getWhere(),
"initializer.exception", c.getName());
}
}
}
if (env.dump()) {
getValue().print(System.out);
System.out.println();
}
}
status = getClassDefinition().getError() ? ERROR : CHECKED;
}
// Initializers (static and instance) must be able to complete normally.
if (isInitializer() && vset.isDeadEnd()) {
env.error(where, "init.no.normal.completion");
vset = vset.clearDeadEnd();
}
return vset;
}
// helper to check(): synthesize a missing super() call
private Expression getDefaultSuperCall(Environment env) {
Expression se = null;
ClassDefinition sclass = getClassDefinition().getSuperClass().getClassDefinition();
// does the superclass constructor require an enclosing instance?
ClassDefinition reqc = (sclass == null) ? null
: sclass.isTopLevel() ? null
: sclass.getOuterClass();
ClassDefinition thisc = getClassDefinition();
if (reqc != null && !Context.outerLinkExists(env, reqc, thisc)) {
se = new SuperExpression(where, new NullExpression(where));
env.error(where, "no.default.outer.arg", reqc, getClassDefinition());
}
if (se == null) {
se = new SuperExpression(where);
}
return new MethodExpression(where, se, idInit, new Expression[0]);
}
/**
* Inline the field
*/
void inline(Environment env) throws ClassNotFound {
switch (status) {
case PARSED:
check(env);
inline(env);
break;
case CHECKED:
if (env.dump()) {
System.out.println("[inline field " + getClassDeclaration().getName() + "." + getName() + "]");
}
status = INLINING;
env = new Environment(env, this);
if (isMethod()) {
if ((!isNative()) && (!isAbstract())) {
Statement s = (Statement)getValue();
Context ctx = new Context((Context)null, this);
for (Enumeration e = args.elements() ; e.hasMoreElements() ;) {
LocalMember local = (LocalMember)e.nextElement();
ctx.declare(env, local);
}
setValue(s.inline(env, ctx));
}
} else if (isInnerClass()) {
// some classes are checked and inlined separately
ClassDefinition nc = getInnerClass();
if (nc instanceof SourceClass && !nc.isLocal()
&& nc.isInsideLocal()) {
status = INLINING;
((SourceClass)nc).inlineLocalClass(env);
}
status = INLINED;
break;
} else {
if (getValue() != null) {
Context ctx = new Context((Context)null, this);
if (!isStatic()) {
// Cf. "thisArg" in SourceClass.checkMembers().
Context ctxInst = new Context(ctx, this);
LocalMember thisArg =
((SourceClass)clazz).getThisArgument();
ctxInst.declare(env, thisArg);
setValue(((Expression)getValue())
.inlineValue(env, ctxInst));
} else {
setValue(((Expression)getValue())
.inlineValue(env, ctx));
}
}
}
if (env.dump()) {
System.out.println("[inlined field " + getClassDeclaration().getName() + "." + getName() + "]");
if (getValue() != null) {
getValue().print(System.out);
System.out.println();
} else {
System.out.println("<empty>");
}
}
status = INLINED;
break;
}
}
/**
* Get the value of the field (or null if the value can't be determined)
*/
public Node getValue(Environment env) throws ClassNotFound {
Node value = getValue();
if (value != null && status != INLINED) {
// be sure to get the imports right:
env = ((SourceClass)clazz).setupEnv(env);
inline(env);
value = (status == INLINED) ? getValue() : null;
}
return value;
}
public boolean isInlineable(Environment env, boolean fromFinal) throws ClassNotFound {
if (super.isInlineable(env, fromFinal)) {
getValue(env);
return (status == INLINED) && !getClassDefinition().getError();
}
return false;
}
/**
* Get the initial value of the field
*/
public Object getInitialValue() {
if (isMethod() || (getValue() == null) || (!isFinal()) || (status != INLINED)) {
return null;
}
return ((Expression)getValue()).getValue();
}
/**
* Generate code
*/
public void code(Environment env, Assembler asm) throws ClassNotFound {
switch (status) {
case PARSED:
check(env);
code(env, asm);
return;
case CHECKED:
inline(env);
code(env, asm);
return;
case INLINED:
// Actually generate code
if (env.dump()) {
System.out.println("[code field " + getClassDeclaration().getName() + "." + getName() + "]");
}
if (isMethod() && (!isNative()) && (!isAbstract())) {
env = new Environment(env, this);
Context ctx = new Context((Context)null, this);
Statement s = (Statement)getValue();
/**代码未完, 请加载全部代码(NowJava.com).**/