0N/A/*
2362N/A * Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.tools.java;
0N/A
0N/Aimport sun.tools.tree.Node;
0N/Aimport sun.tools.tree.Vset;
0N/Aimport sun.tools.tree.Expression;
0N/Aimport sun.tools.tree.Statement;
0N/Aimport sun.tools.tree.Context;
0N/Aimport sun.tools.asm.Assembler;
0N/Aimport java.io.PrintStream;
0N/Aimport java.util.Vector;
0N/Aimport java.util.Map;
0N/Aimport java.util.HashMap;
0N/A
0N/A/**
0N/A * This class defines a member of a Java class:
0N/A * a variable, a method, or an inner class.
0N/A *
0N/A * WARNING: The contents of this source file are not part of any
0N/A * supported API. Code that depends on them does so at its own risk:
0N/A * they are subject to change or removal without notice.
0N/A */
0N/Apublic
0N/Aclass MemberDefinition implements Constants {
0N/A protected long where;
0N/A protected int modifiers;
0N/A protected Type type;
0N/A protected String documentation;
0N/A protected IdentifierToken expIds[];
0N/A protected ClassDeclaration exp[];
0N/A protected Node value;
0N/A protected ClassDefinition clazz;
0N/A protected Identifier name;
0N/A protected ClassDefinition innerClass;
0N/A protected MemberDefinition nextMember;
0N/A protected MemberDefinition nextMatch;
0N/A protected MemberDefinition accessPeer;
0N/A protected boolean superAccessMethod;
0N/A
0N/A /**
0N/A * Constructor
0N/A */
0N/A public MemberDefinition(long where, ClassDefinition clazz, int modifiers,
0N/A Type type, Identifier name,
0N/A IdentifierToken expIds[], Node value) {
0N/A if (expIds == null) {
0N/A expIds = new IdentifierToken[0];
0N/A }
0N/A this.where = where;
0N/A this.clazz = clazz;
0N/A this.modifiers = modifiers;
0N/A this.type = type;
0N/A this.name = name;
0N/A this.expIds = expIds;
0N/A this.value = value;
0N/A }
0N/A
0N/A /**
0N/A * Constructor for an inner class.
0N/A * Inner classes are represented as fields right along with
0N/A * variables and methods for simplicity of data structure,
0N/A * and to reflect properly the textual declaration order.
0N/A * <p>
0N/A * This constructor calls the generic constructor for this
0N/A * class, extracting all necessary values from the innerClass.
0N/A */
0N/A public MemberDefinition(ClassDefinition innerClass) {
0N/A this(innerClass.getWhere(),
0N/A innerClass.getOuterClass(),
0N/A innerClass.getModifiers(),
0N/A innerClass.getType(),
0N/A innerClass.getName().getFlatName().getName(),
0N/A null, null);
0N/A this.innerClass = innerClass;
0N/A }
0N/A
0N/A /**
0N/A * A cache of previously created proxy members. Used to ensure
0N/A * uniqueness of proxy objects. See the makeProxyMember method
0N/A * defined below.
0N/A */
0N/A static private Map proxyCache;
0N/A
0N/A /**
0N/A * Create a member which is externally the same as `field' but
0N/A * is defined in class `classDef'. This is used by code
0N/A * in sun.tools.tree.(MethodExpression,FieldExpression) as
0N/A * part of the fix for bug 4135692.
0N/A *
0N/A * Proxy members should not be added, ala addMember(), to classes.
0N/A * They are merely "stand-ins" to produce modified MethodRef
0N/A * constant pool entries during code generation.
0N/A *
0N/A * We keep a cache of previously created proxy members not to
0N/A * save time or space, but to ensure uniqueness of the proxy
0N/A * member for any (field,classDef) pair. If these are not made
0N/A * unique then we can end up generating duplicate MethodRef
0N/A * constant pool entries during code generation.
0N/A */
0N/A public static MemberDefinition makeProxyMember(MemberDefinition field,
0N/A ClassDefinition classDef,
0N/A Environment env) {
0N/A
0N/A if (proxyCache == null) {
0N/A proxyCache = new HashMap();
0N/A }
0N/A
0N/A String key = field.toString() + "@" + classDef.toString();
0N/A // System.out.println("Key is : " + key);
0N/A MemberDefinition proxy = (MemberDefinition)proxyCache.get(key);
0N/A
0N/A if (proxy != null)
0N/A return proxy;
0N/A
0N/A proxy = new MemberDefinition(field.getWhere(), classDef,
0N/A field.getModifiers(), field.getType(),
0N/A field.getName(), field.getExceptionIds(),
0N/A null);
0N/A proxy.exp = field.getExceptions(env);
0N/A proxyCache.put(key, proxy);
0N/A
0N/A return proxy;
0N/A }
0N/A
0N/A /**
0N/A * Get the position in the input
0N/A */
0N/A public final long getWhere() {
0N/A return where;
0N/A }
0N/A
0N/A /**
0N/A * Get the class declaration
0N/A */
0N/A public final ClassDeclaration getClassDeclaration() {
0N/A return clazz.getClassDeclaration();
0N/A }
0N/A
0N/A /**
0N/A * A stub. Subclasses can do more checking.
0N/A */
0N/A public void resolveTypeStructure(Environment env) {
0N/A }
0N/A
0N/A /**
0N/A * Get the class declaration in which the field is actually defined
0N/A */
0N/A public ClassDeclaration getDefiningClassDeclaration() {
0N/A return getClassDeclaration();
0N/A }
0N/A
0N/A /**
0N/A * Get the class definition
0N/A */
0N/A public final ClassDefinition getClassDefinition() {
0N/A return clazz;
0N/A }
0N/A
0N/A /**
0N/A * Get the field's top-level enclosing class
0N/A */
0N/A public final ClassDefinition getTopClass() {
0N/A return clazz.getTopClass();
0N/A }
0N/A
0N/A /**
0N/A * Get the field's modifiers
0N/A */
0N/A public final int getModifiers() {
0N/A return modifiers;
0N/A }
0N/A public final void subModifiers(int mod) {
0N/A modifiers &= ~mod;
0N/A }
0N/A public final void addModifiers(int mod) {
0N/A modifiers |= mod;
0N/A }
0N/A
0N/A /**
0N/A * Get the field's type
0N/A */
0N/A public final Type getType() {
0N/A return type;
0N/A }
0N/A
0N/A /**
0N/A * Get the field's name
0N/A */
0N/A public final Identifier getName() {
0N/A return name;
0N/A }
0N/A
0N/A /**
0N/A * Get arguments (a vector of LocalMember)
0N/A */
0N/A public Vector getArguments() {
0N/A return isMethod() ? new Vector() : null;
0N/A }
0N/A
0N/A /**
0N/A * Get the exceptions that are thrown by this method.
0N/A */
0N/A public ClassDeclaration[] getExceptions(Environment env) {
0N/A if (expIds != null && exp == null) {
0N/A if (expIds.length == 0)
0N/A exp = new ClassDeclaration[0];
0N/A else
0N/A // we should have translated this already!
0N/A throw new CompilerError("getExceptions "+this);
0N/A }
0N/A return exp;
0N/A }
0N/A
0N/A public final IdentifierToken[] getExceptionIds() {
0N/A return expIds;
0N/A }
0N/A
0N/A /**
0N/A * Get an inner class.
0N/A */
0N/A public ClassDefinition getInnerClass() {
0N/A return innerClass;
0N/A }
0N/A
0N/A /**
0N/A * Is this a synthetic field which holds a copy of,
0N/A * or reference to, a local variable or enclosing instance?
0N/A */
0N/A public boolean isUplevelValue() {
0N/A if (!isSynthetic() || !isVariable() || isStatic()) {
0N/A return false;
0N/A }
0N/A String name = this.name.toString();
0N/A return name.startsWith(prefixVal)
0N/A || name.toString().startsWith(prefixLoc)
0N/A || name.toString().startsWith(prefixThis);
0N/A }
0N/A
0N/A public boolean isAccessMethod() {
0N/A // This no longer works, because access methods
0N/A // for constructors do not use the standard naming
0N/A // scheme.
0N/A // return isSynthetic() && isMethod()
0N/A // && name.toString().startsWith(prefixAccess);
0N/A // Assume that a method is an access method if it has
0N/A // an access peer. NOTE: An access method will not be
0N/A // recognized as such until 'setAccessMethodTarget' has
0N/A // been called on it.
0N/A return isSynthetic() && isMethod() && (accessPeer != null);
0N/A }
0N/A
0N/A /**
0N/A * Is this a synthetic method which provides access to a
0N/A * visible private member?
0N/A */
0N/A public MemberDefinition getAccessMethodTarget() {
0N/A if (isAccessMethod()) {
0N/A for (MemberDefinition f = accessPeer; f != null; f = f.accessPeer) {
0N/A // perhaps skip over another access for the same field
0N/A if (!f.isAccessMethod()) {
0N/A return f;
0N/A }
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A
0N/A public void setAccessMethodTarget(MemberDefinition target) {
0N/A if (getAccessMethodTarget() != target) {
0N/A /*-------------------*
0N/A if (!isAccessMethod() || accessPeer != null ||
0N/A target.accessPeer != null) {
0N/A throw new CompilerError("accessPeer");
0N/A }
0N/A *-------------------*/
0N/A if (accessPeer != null || target.accessPeer != null) {
0N/A throw new CompilerError("accessPeer");
0N/A }
0N/A accessPeer = target;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * If this method is a getter for a private field, return the setter.
0N/A */
0N/A public MemberDefinition getAccessUpdateMember() {
0N/A if (isAccessMethod()) {
0N/A for (MemberDefinition f = accessPeer; f != null; f = f.accessPeer) {
0N/A if (f.isAccessMethod()) {
0N/A return f;
0N/A }
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A public void setAccessUpdateMember(MemberDefinition updater) {
0N/A if (getAccessUpdateMember() != updater) {
0N/A if (!isAccessMethod() ||
0N/A updater.getAccessMethodTarget() != getAccessMethodTarget()) {
0N/A throw new CompilerError("accessPeer");
0N/A }
0N/A updater.accessPeer = accessPeer;
0N/A accessPeer = updater;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Is this an access method for a field selection or method call
0N/A * of the form '...super.foo' or '...super.foo()'?
0N/A */
0N/A public final boolean isSuperAccessMethod() {
0N/A return superAccessMethod;
0N/A }
0N/A
0N/A /**
0N/A * Mark this member as an access method for a field selection
0N/A * or method call via the 'super' keyword.
0N/A */
0N/A public final void setIsSuperAccessMethod(boolean b) {
0N/A superAccessMethod = b;
0N/A }
0N/A
0N/A /**
0N/A * Tell if this is a final variable without an initializer.
0N/A * Such variables are subject to definite single assignment.
0N/A */
0N/A public final boolean isBlankFinal() {
0N/A return isFinal() && !isSynthetic() && getValue() == null;
0N/A }
0N/A
0N/A public boolean isNeverNull() {
0N/A if (isUplevelValue()) {
0N/A // loc$x and this$C are never null
0N/A return !name.toString().startsWith(prefixVal);
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Get the field's final value (may return null)
0N/A */
0N/A public Node getValue(Environment env) throws ClassNotFound {
0N/A return value;
0N/A }
0N/A public final Node getValue() {
0N/A return value;
0N/A }
0N/A public final void setValue(Node value) {
0N/A this.value = value;
0N/A }
0N/A public Object getInitialValue() {
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Get the next field or the next match
0N/A */
0N/A public final MemberDefinition getNextMember() {
0N/A return nextMember;
0N/A }
0N/A public final MemberDefinition getNextMatch() {
0N/A return nextMatch;
0N/A }
0N/A
0N/A /**
0N/A * Get the field's documentation
0N/A */
0N/A public String getDocumentation() {
0N/A return documentation;
0N/A }
0N/A
0N/A /**
0N/A * Request a check of the field definition.
0N/A */
0N/A public void check(Environment env) throws ClassNotFound {
0N/A }
0N/A
0N/A /**
0N/A * Really check the field definition.
0N/A */
0N/A public Vset check(Environment env, Context ctx, Vset vset) throws ClassNotFound {
0N/A return vset;
0N/A }
0N/A
0N/A /**
0N/A * Generate code
0N/A */
0N/A public void code(Environment env, Assembler asm) throws ClassNotFound {
0N/A throw new CompilerError("code");
0N/A }
0N/A public void codeInit(Environment env, Context ctx, Assembler asm) throws ClassNotFound {
0N/A throw new CompilerError("codeInit");
0N/A }
0N/A
0N/A /**
0N/A * Tells whether to report a deprecation error for this field.
0N/A */
0N/A public boolean reportDeprecated(Environment env) {
0N/A return (isDeprecated() || clazz.reportDeprecated(env));
0N/A }
0N/A
0N/A /**
0N/A * Check if a field can reach another field (only considers
0N/A * forward references, not the access modifiers).
0N/A */
0N/A public final boolean canReach(Environment env, MemberDefinition f) {
0N/A if (f.isLocal() || !f.isVariable() || !(isVariable() || isInitializer()))
0N/A return true;
0N/A if ((getClassDeclaration().equals(f.getClassDeclaration())) &&
0N/A (isStatic() == f.isStatic())) {
0N/A // They are located in the same class, and are either both
0N/A // static or both non-static. Check the initialization order.
0N/A while (((f = f.getNextMember()) != null) && (f != this));
0N/A return f != null;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A //-----------------------------------------------------------------
0N/A // The code in this section is intended to test certain kinds of
0N/A // compatibility between methods. There are two kinds of compatibility
0N/A // that the compiler may need to test. The first is whether one
0N/A // method can legally override another. The second is whether two
0N/A // method definitions can legally coexist. We use the word `meet'
0N/A // to mean the intersection of two legally coexisting methods.
0N/A // For more information on these kinds of compatibility, see the
0N/A // comments/code for checkOverride() and checkMeet() below.
0N/A
0N/A /**
0N/A * Constants used by getAccessLevel() to represent the access
0N/A * modifiers as numbers.
0N/A */
0N/A static final int PUBLIC_ACCESS = 1;
0N/A static final int PROTECTED_ACCESS = 2;
0N/A static final int PACKAGE_ACCESS = 3;
0N/A static final int PRIVATE_ACCESS = 4;
0N/A
0N/A /**
0N/A * Return the access modifier of this member as a number. The idea
0N/A * is that this number may be used to check properties like "the
0N/A * access modifier of x is more restrictive than the access
0N/A * modifier of y" with a simple inequality test:
0N/A * "x.getAccessLevel() > y.getAccessLevel.
0N/A *
0N/A * This is an internal utility method.
0N/A */
0N/A private int getAccessLevel() {
0N/A // Could just compute this once instead of recomputing.
0N/A // Check to see if this is worth it.
0N/A if (isPublic()) {
0N/A return PUBLIC_ACCESS;
0N/A } else if (isProtected()) {
0N/A return PROTECTED_ACCESS;
0N/A } else if (isPackagePrivate()) {
0N/A return PACKAGE_ACCESS;
0N/A } else if (isPrivate()) {
0N/A return PRIVATE_ACCESS;
0N/A } else {
0N/A throw new CompilerError("getAccessLevel()");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Munge our error message to report whether the override conflict
0N/A * came from an inherited method or a declared method.
0N/A */
0N/A private void reportError(Environment env, String errorString,
0N/A ClassDeclaration clazz,
0N/A MemberDefinition method) {
0N/A
0N/A if (clazz == null) {
0N/A // For example:
0N/A // "Instance method BLAH inherited from CLASSBLAH1 cannot be
0N/A // overridden by the static method declared in CLASSBLAH2."
0N/A env.error(getWhere(), errorString,
0N/A this, getClassDeclaration(),
0N/A method.getClassDeclaration());
0N/A } else {
0N/A // For example:
0N/A // "In CLASSBLAH1, instance method BLAH inherited from CLASSBLAH2
0N/A // cannot be overridden by the static method inherited from
0N/A // CLASSBLAH3."
0N/A env.error(clazz.getClassDefinition().getWhere(),
0N/A //"inherit." + errorString,
0N/A errorString,
0N/A //clazz,
0N/A this, getClassDeclaration(),
0N/A method.getClassDeclaration());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Convenience method to see if two methods return the same type
0N/A */
0N/A public boolean sameReturnType(MemberDefinition method) {
0N/A // Make sure both are methods.
0N/A if (!isMethod() || !method.isMethod()) {
0N/A throw new CompilerError("sameReturnType: not method");
0N/A }
0N/A
0N/A Type myReturnType = getType().getReturnType();
0N/A Type yourReturnType = method.getType().getReturnType();
0N/A
0N/A return (myReturnType == yourReturnType);
0N/A }
0N/A
0N/A /**
0N/A * Check to see if `this' can override/hide `method'. Caller is
0N/A * responsible for verifying that `method' has the same signature
0N/A * as `this'. Caller is also responsible for verifying that
0N/A * `method' is visible to the class where this override is occurring.
0N/A * This method is called for the case when class B extends A and both
0N/A * A and B define some method.
0N/A * <pre>
0N/A * A - void foo() throws e1
0N/A * |
0N/A * |
0N/A * B - void foo() throws e2
0N/A * </pre>
0N/A */
0N/A public boolean checkOverride(Environment env, MemberDefinition method) {
0N/A return checkOverride(env, method, null);
0N/A }
0N/A
0N/A /**
0N/A * Checks whether `this' can override `method'. It `clazz' is
0N/A * null, it reports the errors in the class where `this' is
0N/A * declared. If `clazz' is not null, it reports the error in `clazz'.
0N/A */
0N/A private boolean checkOverride(Environment env,
0N/A MemberDefinition method,
0N/A ClassDeclaration clazz) {
0N/A // This section of code is largely based on section 8.4.6.3
0N/A // of the JLS.
0N/A
0N/A boolean success = true;
0N/A
0N/A // Sanity
0N/A if (!isMethod()) {
0N/A throw new CompilerError("checkOverride(), expected method");
0N/A }
0N/A
0N/A // Suppress checks for synthetic methods, as the compiler presumably
0N/A // knows what it is doing, e.g., access methods.
0N/A if (isSynthetic()) {
0N/A // Sanity check: We generally do not intend for one synthetic
0N/A // method to override another, though hiding of static members
0N/A // is expected. This check may need to be changed if new uses
0N/A // of synthetic methods are devised.
0N/A //
0N/A // Query: this code was copied from elsewhere. What
0N/A // exactly is the role of the !isStatic() in the test?
0N/A if (method.isFinal() ||
0N/A (!method.isConstructor() &&
0N/A !method.isStatic() && !isStatic())) {
0N/A ////////////////////////////////////////////////////////////
0N/A // NMG 2003-01-28 removed the following test because it is
0N/A // invalidated by bridge methods inserted by the "generic"
0N/A // (1.5) Java compiler. In 1.5, this code is used,
0N/A // indirectly, by rmic
0N/A ////////////////////////////////////////////////////////////
0N/A // throw new CompilerError("checkOverride() synthetic");
0N/A ////////////////////////////////////////////////////////////
0N/A }
0N/A
0N/A // We trust the compiler. (Ha!) We're done checking.
0N/A return true;
0N/A }
0N/A
0N/A // Our caller should have verified that the method had the
0N/A // same signature.
0N/A if (getName() != method.getName() ||
0N/A !getType().equalArguments(method.getType())) {
0N/A
0N/A throw new CompilerError("checkOverride(), signature mismatch");
0N/A }
0N/A
0N/A // It is forbidden to `override' a static method with an instance
0N/A // method.
0N/A if (method.isStatic() && !isStatic()) {
0N/A reportError(env, "override.static.with.instance", clazz, method);
0N/A success = false;
0N/A }
0N/A
0N/A // It is forbidden to `hide' an instance method with a static
0N/A // method.
0N/A if (!method.isStatic() && isStatic()) {
0N/A reportError(env, "hide.instance.with.static", clazz, method);
0N/A success = false;
0N/A }
0N/A
0N/A // We cannot override a final method.
0N/A if (method.isFinal()) {
0N/A reportError(env, "override.final.method", clazz, method);
0N/A success = false;
0N/A }
0N/A
0N/A // Give a warning when we override a deprecated method with
0N/A // a non-deprecated one.
0N/A //
0N/A // We bend over backwards to suppress this warning if
0N/A // the `method' has not been already compiled or
0N/A // `this' has been already compiled.
0N/A if (method.reportDeprecated(env) && !isDeprecated()
0N/A && this instanceof sun.tools.javac.SourceMember) {
0N/A reportError(env, "warn.override.is.deprecated",
0N/A clazz, method);
0N/A }
0N/A
0N/A // Visibility may not be more restrictive
0N/A if (getAccessLevel() > method.getAccessLevel()) {
0N/A reportError(env, "override.more.restrictive", clazz, method);
0N/A success = false;
0N/A }
0N/A
0N/A // Return type equality
0N/A if (!sameReturnType(method)) {
0N/A ////////////////////////////////////////////////////////////
0N/A // PCJ 2003-07-30 removed the following error because it is
0N/A // invalidated by the covariant return type feature of the
0N/A // 1.5 compiler. The resulting check is now much looser
0N/A // than the actual 1.5 language spec, but that should be OK
0N/A // because this code is only still used by rmic. See 4892308.
0N/A ////////////////////////////////////////////////////////////
0N/A // reportError(env, "override.different.return", clazz, method);
0N/A // success = false;
0N/A ////////////////////////////////////////////////////////////
0N/A }
0N/A
0N/A // Exception agreeement
0N/A if (!exceptionsFit(env, method)) {
0N/A reportError(env, "override.incompatible.exceptions",
0N/A clazz, method);
0N/A success = false;
0N/A }
0N/A
0N/A return success;
0N/A }
0N/A
0N/A /**
0N/A * Check to see if two method definitions are compatible, that is
0N/A * do they have a `meet'. The meet of two methods is essentially
0N/A * and `intersection' of
0N/A * two methods. This method is called when some class C inherits
0N/A * declarations for some method foo from two parents (superclass,
0N/A * interfaces) but it does not, itself, have a declaration of foo.
0N/A * Caller is responsible for making sure that both methods are
0N/A * indeed visible in clazz.
0N/A * <pre>
0N/A * A - void foo() throws e1
0N/A * \
0N/A * \ B void foo() throws e2
0N/A * \ /
0N/A * \ /
0N/A * C
0N/A * </pre>
0N/A */
0N/A public boolean checkMeet(Environment env,
0N/A MemberDefinition method,
0N/A ClassDeclaration clazz) {
0N/A // This section of code is largely based on Section 8.4.6
0N/A // and 9.4.1 of the JLS.
0N/A
0N/A // Sanity
0N/A if (!isMethod()) {
0N/A throw new CompilerError("checkMeet(), expected method");
0N/A }
0N/A
0N/A // Check for both non-abstract.
0N/A if (!isAbstract() && !method.isAbstract()) {
0N/A throw new CompilerError("checkMeet(), no abstract method");
0N/A }
0N/A
0N/A // If either method is non-abstract, then we need to check that
0N/A // the abstract method can be properly overridden. We call
0N/A // the checkOverride method to check this and generate any errors.
0N/A // This test must follow the previous test.
0N/A else if (!isAbstract()) {
0N/A return checkOverride(env, method, clazz);
0N/A } else if (!method.isAbstract()) {
0N/A return method.checkOverride(env, this, clazz);
0N/A }
0N/A
0N/A // Both methods are abstract.
0N/A
0N/A // Our caller should have verified that the method has the
0N/A // same signature.
0N/A if (getName() != method.getName() ||
0N/A !getType().equalArguments(method.getType())) {
0N/A
0N/A throw new CompilerError("checkMeet(), signature mismatch");
0N/A }
0N/A
0N/A // Check for return type equality
0N/A if (!sameReturnType(method)) {
0N/A // More args?
0N/A env.error(clazz.getClassDefinition().getWhere(),
0N/A "meet.different.return",
0N/A this, this.getClassDeclaration(),
0N/A method.getClassDeclaration());
0N/A return false;
0N/A }
0N/A
0N/A // We don't have to check visibility -- there always
0N/A // potentially exists a meet. Similarly with exceptions.
0N/A
0N/A // There does exist a meet.
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * This method is meant to be used to determine if one of two inherited
0N/A * methods could override the other. Unlike checkOverride(), failure
0N/A * is not an error. This method is only meant to be called after
0N/A * checkMeet() has succeeded on the two methods.
0N/A *
0N/A * If you call couldOverride() without doing a checkMeet() first, then
0N/A * you are on your own.
0N/A */
0N/A public boolean couldOverride(Environment env,
0N/A MemberDefinition method) {
0N/A
0N/A // Sanity
0N/A if (!isMethod()) {
0N/A throw new CompilerError("coulcOverride(), expected method");
0N/A }
0N/A
0N/A // couldOverride() is only called with `this' and `method' both
0N/A // being inherited methods. Neither of them is defined in the
0N/A // class which we are currently working on. Even though an
0N/A // abstract method defined *in* a class can override a non-abstract
0N/A // method defined in a superclass, an abstract method inherited
0N/A // from an interface *never* can override a non-abstract method.
0N/A // This comment may sound odd, but that's the way inheritance is.
0N/A // The following check makes sure we aren't trying to override
0N/A // an inherited non-abstract definition with an abstract definition
0N/A // from an interface.
0N/A if (!method.isAbstract()) {
0N/A return false;
0N/A }
0N/A
0N/A // Visibility should be less restrictive
0N/A if (getAccessLevel() > method.getAccessLevel()) {
0N/A return false;
0N/A }
0N/A
0N/A // Exceptions
0N/A if (!exceptionsFit(env, method)) {
0N/A return false;
0N/A }
0N/A
0N/A // Potentially some deprecation warnings could be given here
0N/A // when we merge two abstract methods, one of which is deprecated.
0N/A // This is not currently reported.
0N/A
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Check to see if the exceptions of `this' fit within the
0N/A * exceptions of `method'.
0N/A */
0N/A private boolean exceptionsFit(Environment env,
0N/A MemberDefinition method) {
0N/A ClassDeclaration e1[] = getExceptions(env); // my exceptions
0N/A ClassDeclaration e2[] = method.getExceptions(env); // parent's
0N/A
0N/A // This code is taken nearly verbatim from the old implementation
0N/A // of checkOverride() in SourceClass.
0N/A outer:
0N/A for (int i = 0 ; i < e1.length ; i++) {
0N/A try {
0N/A ClassDefinition c1 = e1[i].getClassDefinition(env);
0N/A for (int j = 0 ; j < e2.length ; j++) {
0N/A if (c1.subClassOf(env, e2[j])) {
0N/A continue outer;
0N/A }
0N/A }
0N/A if (c1.subClassOf(env,
0N/A env.getClassDeclaration(idJavaLangError)))
0N/A continue outer;
0N/A if (c1.subClassOf(env,
0N/A env.getClassDeclaration(idJavaLangRuntimeException)))
0N/A continue outer;
0N/A
0N/A // the throws was neither something declared by a parent,
0N/A // nor one of the ignorables.
0N/A return false;
0N/A
0N/A } catch (ClassNotFound ee) {
0N/A // We were unable to find one of the exceptions.
0N/A env.error(getWhere(), "class.not.found",
0N/A ee.name, method.getClassDeclaration());
0N/A }
0N/A }
0N/A
0N/A // All of the exceptions `fit'.
0N/A return true;
0N/A }
0N/A
0N/A //-----------------------------------------------------------------
0N/A
0N/A /**
0N/A * Checks
0N/A */
0N/A public final boolean isPublic() {
0N/A return (modifiers & M_PUBLIC) != 0;
0N/A }
0N/A public final boolean isPrivate() {
0N/A return (modifiers & M_PRIVATE) != 0;
0N/A }
0N/A public final boolean isProtected() {
0N/A return (modifiers & M_PROTECTED) != 0;
0N/A }
0N/A public final boolean isPackagePrivate() {
0N/A return (modifiers & (M_PUBLIC | M_PRIVATE | M_PROTECTED)) == 0;
0N/A }
0N/A public final boolean isFinal() {
0N/A return (modifiers & M_FINAL) != 0;
0N/A }
0N/A public final boolean isStatic() {
0N/A return (modifiers & M_STATIC) != 0;
0N/A }
0N/A public final boolean isSynchronized() {
0N/A return (modifiers & M_SYNCHRONIZED) != 0;
0N/A }
0N/A public final boolean isAbstract() {
0N/A return (modifiers & M_ABSTRACT) != 0;
0N/A }
0N/A public final boolean isNative() {
0N/A return (modifiers & M_NATIVE) != 0;
0N/A }
0N/A public final boolean isVolatile() {
0N/A return (modifiers & M_VOLATILE) != 0;
0N/A }
0N/A public final boolean isTransient() {
0N/A return (modifiers & M_TRANSIENT) != 0;
0N/A }
0N/A public final boolean isMethod() {
0N/A return type.isType(TC_METHOD);
0N/A }
0N/A public final boolean isVariable() {
0N/A return !type.isType(TC_METHOD) && innerClass == null;
0N/A }
0N/A public final boolean isSynthetic() {
0N/A return (modifiers & M_SYNTHETIC) != 0;
0N/A }
0N/A public final boolean isDeprecated() {
0N/A return (modifiers & M_DEPRECATED) != 0;
0N/A }
0N/A public final boolean isStrict() {
0N/A return (modifiers & M_STRICTFP) != 0;
0N/A }
0N/A public final boolean isInnerClass() {
0N/A return innerClass != null;
0N/A }
0N/A public final boolean isInitializer() {
0N/A return getName().equals(idClassInit);
0N/A }
0N/A public final boolean isConstructor() {
0N/A return getName().equals(idInit);
0N/A }
0N/A public boolean isLocal() {
0N/A return false;
0N/A }
0N/A public boolean isInlineable(Environment env, boolean fromFinal) throws ClassNotFound {
0N/A return (isStatic() || isPrivate() || isFinal() || isConstructor() || fromFinal) &&
0N/A !(isSynchronized() || isNative());
0N/A }
0N/A
0N/A /**
0N/A * Check if constant: Will it inline away to a constant?
0N/A */
0N/A public boolean isConstant() {
0N/A if (isFinal() && isVariable() && value != null) {
0N/A try {
0N/A // If an infinite regress requeries this name,
0N/A // deny that it is a constant.
0N/A modifiers &= ~M_FINAL;
0N/A return ((Expression)value).isConstant();
0N/A } finally {
0N/A modifiers |= M_FINAL;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * toString
0N/A */
0N/A public String toString() {
0N/A Identifier name = getClassDefinition().getName();
0N/A if (isInitializer()) {
0N/A return isStatic() ? "static {}" : "instance {}";
0N/A } else if (isConstructor()) {
0N/A StringBuffer buf = new StringBuffer();
0N/A buf.append(name);
0N/A buf.append('(');
0N/A Type argTypes[] = getType().getArgumentTypes();
0N/A for (int i = 0 ; i < argTypes.length ; i++) {
0N/A if (i > 0) {
0N/A buf.append(',');
0N/A }
0N/A buf.append(argTypes[i].toString());
0N/A }
0N/A buf.append(')');
0N/A return buf.toString();
0N/A } else if (isInnerClass()) {
0N/A return getInnerClass().toString();
0N/A }
0N/A return type.typeString(getName().toString());
0N/A }
0N/A
0N/A /**
0N/A * Print for debugging
0N/A */
0N/A public void print(PrintStream out) {
0N/A if (isPublic()) {
0N/A out.print("public ");
0N/A }
0N/A if (isPrivate()) {
0N/A out.print("private ");
0N/A }
0N/A if (isProtected()) {
0N/A out.print("protected ");
0N/A }
0N/A if (isFinal()) {
0N/A out.print("final ");
0N/A }
0N/A if (isStatic()) {
0N/A out.print("static ");
0N/A }
0N/A if (isSynchronized()) {
0N/A out.print("synchronized ");
0N/A }
0N/A if (isAbstract()) {
0N/A out.print("abstract ");
0N/A }
0N/A if (isNative()) {
0N/A out.print("native ");
0N/A }
0N/A if (isVolatile()) {
0N/A out.print("volatile ");
0N/A }
0N/A if (isTransient()) {
0N/A out.print("transient ");
0N/A }
0N/A out.println(toString() + ";");
0N/A }
0N/A
0N/A public void cleanup(Environment env) {
0N/A documentation = null;
0N/A if (isMethod() && value != null) {
0N/A int cost = 0;
0N/A if (isPrivate() || isInitializer()) {
0N/A value = Statement.empty;
0N/A } else if ((cost =
0N/A ((Statement)value)
0N/A .costInline(Statement.MAXINLINECOST, null, null))
0N/A >= Statement.MAXINLINECOST) {
0N/A // will never be inlined
0N/A value = Statement.empty;
0N/A } else {
0N/A try {
0N/A if (!isInlineable(null, true)) {
0N/A value = Statement.empty;
0N/A }
0N/A }
0N/A catch (ClassNotFound ee) { }
0N/A }
0N/A if (value != Statement.empty && env.dump()) {
0N/A env.output("[after cleanup of " + getName() + ", " +
0N/A cost + " expression cost units remain]");
0N/A }
0N/A } else if (isVariable()) {
0N/A if (isPrivate() || !isFinal() || type.isType(TC_ARRAY)) {
0N/A value = null;
0N/A }
0N/A }
0N/A }
0N/A}