/*
 * 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.tree.CompoundStatement;
import sun.tools.asm.Assembler;
import sun.tools.asm.ConstantPool;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.io.IOException;
import java.io.OutputStream;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;

This class represents an Java class as it is read from an Java source file. 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.
/** * This class represents an Java class as it is read from * an Java source file. * * 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 SourceClass extends ClassDefinition {
The toplevel environment, shared with the parser
/** * The toplevel environment, shared with the parser */
Environment toplevelEnv;
The default constructor
/** * The default constructor */
SourceMember defConstructor;
The constant pool
/** * The constant pool */
ConstantPool tab = new ConstantPool();
The list of class dependencies
/** * The list of class dependencies */
Hashtable deps = new Hashtable(11);
The field used to represent "this" in all of my code.
/** * The field used to represent "this" in all of my code. */
LocalMember thisArg;
Last token of class, as reported by parser.
/** * Last token of class, as reported by parser. */
long endPosition;
Access methods for constructors are distinguished from the constructors themselves by a dummy first argument. A unique type used for this purpose and shared by all constructor access methods within a package-member class is maintained here.

This field is null except in an outermost class containing one or more classes needing such an access method.

/** * Access methods for constructors are distinguished from * the constructors themselves by a dummy first argument. * A unique type used for this purpose and shared by all * constructor access methods within a package-member class is * maintained here. * <p> * This field is null except in an outermost class containing * one or more classes needing such an access method. */
private Type dummyArgumentType = null;
Constructor
/** * Constructor */
public SourceClass(Environment env, long where, ClassDeclaration declaration, String documentation, int modifiers, IdentifierToken superClass, IdentifierToken interfaces[], SourceClass outerClass, Identifier localName) { super(env.getSource(), where, declaration, modifiers, superClass, interfaces); setOuterClass(outerClass); this.toplevelEnv = env; this.documentation = documentation; if (ClassDefinition.containsDeprecated(documentation)) { this.modifiers |= M_DEPRECATED; } // Check for a package level class which is declared static. if (isStatic() && outerClass == null) { env.error(where, "static.class", this); this.modifiers &=~ M_STATIC; } // Inner classes cannot be static, nor can they be interfaces // (which are implicitly static). Static classes and interfaces // can only occur as top-level entities. // // Note that we do not have to check for local classes declared // to be static (this is currently caught by the parser) but // we check anyway in case the parser is modified to allow this. if (isLocal() || (outerClass != null && !outerClass.isTopLevel())) { if (isInterface()) { env.error(where, "inner.interface"); } else if (isStatic()) { env.error(where, "static.inner.class", this); this.modifiers &=~ M_STATIC; if (innerClassMember != null) { innerClassMember.subModifiers(M_STATIC); } } } if (isPrivate() && outerClass == null) { env.error(where, "private.class", this); this.modifiers &=~ M_PRIVATE; } if (isProtected() && outerClass == null) { env.error(where, "protected.class", this); this.modifiers &=~ M_PROTECTED; } /*----* if ((isPublic() || isProtected()) && isInsideLocal()) { env.error(where, "warn.public.local.class", this); } *----*/ // maybe define an uplevel "A.this" current instance field if (!isTopLevel() && !isLocal()) { LocalMember outerArg = ((SourceClass)outerClass).getThisArgument(); UplevelReference r = getReference(outerArg); setOuterMember(r.getLocalField(env)); } // Set simple, unmangled local name for a local or anonymous class. // NOTE: It would be OK to do this unconditionally, as null is the // correct value for a member (non-local) class. if (localName != null) setLocalName(localName); // Check for inner class with same simple name as one of // its enclosing classes. Note that 'getLocalName' returns // the simple, unmangled source-level name of any class. // The previous version of this code was not careful to avoid // mangled local class names. This version fixes 4047746. Identifier thisName = getLocalName(); if (thisName != idNull) { // Test above suppresses error for nested anonymous classes, // which have an internal "name", but are not named in source code. for (ClassDefinition scope = outerClass; scope != null; scope = scope.getOuterClass()) { Identifier outerName = scope.getLocalName(); if (thisName.equals(outerName)) env.error(where, "inner.redefined", thisName); } } }
Return last position in this class.
See Also:
  • getWhere
/** * Return last position in this class. * @see #getWhere */
public long getEndPosition() { return endPosition; } public void setEndPosition(long endPosition) { this.endPosition = endPosition; } // JCOV
Return absolute name of source file
/** * Return absolute name of source file */
public String getAbsoluteName() { String AbsName = ((ClassFile)getSource()).getAbsoluteName(); return AbsName; } //end JCOV
Return imports
/** * Return imports */
public Imports getImports() { return toplevelEnv.getImports(); }
Find or create my "this" argument, which is used for all methods.
/** * Find or create my "this" argument, which is used for all methods. */
public LocalMember getThisArgument() { if (thisArg == null) { thisArg = new LocalMember(where, this, 0, getType(), idThis); } return thisArg; }
Add a dependency
/** * Add a dependency */
public void addDependency(ClassDeclaration c) { if (tab != null) { tab.put(c); } // If doing -xdepend option, save away list of class dependencies // making sure to NOT include duplicates or the class we are in // (Hashtable's put() makes sure we don't have duplicates) if ( toplevelEnv.print_dependencies() && c != getClassDeclaration() ) { deps.put(c,c); } }
Add a field (check it first)
/** * Add a field (check it first) */
public void addMember(Environment env, MemberDefinition f) { // Make sure the access permissions are self-consistent: switch (f.getModifiers() & (M_PUBLIC | M_PRIVATE | M_PROTECTED)) { case M_PUBLIC: case M_PRIVATE: case M_PROTECTED: case 0: break; default: env.error(f.getWhere(), "inconsistent.modifier", f); // Cut out the more restrictive modifier(s): if (f.isPublic()) { f.subModifiers(M_PRIVATE | M_PROTECTED); } else { f.subModifiers(M_PRIVATE); } break; } // Note exemption for synthetic members below. if (f.isStatic() && !isTopLevel() && !f.isSynthetic()) { if (f.isMethod()) { env.error(f.getWhere(), "static.inner.method", f, this); f.subModifiers(M_STATIC); } else if (f.isVariable()) { if (!f.isFinal() || f.isBlankFinal()) { env.error(f.getWhere(), "static.inner.field", f.getName(), this); f.subModifiers(M_STATIC); } // Even if a static passes this test, there is still another // check in 'SourceMember.check'. The check is delayed so // that the initializer may be inspected more closely, using // 'isConstant()'. Part of fix for 4095568. } else { // Static inner classes are diagnosed in 'SourceClass.<init>'. f.subModifiers(M_STATIC); } } if (f.isMethod()) { if (f.isConstructor()) { if (f.getClassDefinition().isInterface()) { env.error(f.getWhere(), "intf.constructor"); return; } if (f.isNative() || f.isAbstract() || f.isStatic() || f.isSynchronized() || f.isFinal()) { env.error(f.getWhere(), "constr.modifier", f); f.subModifiers(M_NATIVE | M_ABSTRACT | M_STATIC | M_SYNCHRONIZED | M_FINAL); } } else if (f.isInitializer()) { if (f.getClassDefinition().isInterface()) { env.error(f.getWhere(), "intf.initializer"); return; } } // f is not allowed to return an array of void if ((f.getType().getReturnType()).isVoidArray()) { env.error(f.getWhere(), "void.array"); } if (f.getClassDefinition().isInterface() && (f.isStatic() || f.isSynchronized() || f.isNative() || f.isFinal() || f.isPrivate() || f.isProtected())) { env.error(f.getWhere(), "intf.modifier.method", f); f.subModifiers(M_STATIC | M_SYNCHRONIZED | M_NATIVE | M_FINAL | M_PRIVATE); } if (f.isTransient()) { env.error(f.getWhere(), "transient.meth", f); f.subModifiers(M_TRANSIENT); } if (f.isVolatile()) { env.error(f.getWhere(), "volatile.meth", f); f.subModifiers(M_VOLATILE); } if (f.isAbstract()) { if (f.isPrivate()) { env.error(f.getWhere(), "abstract.private.modifier", f); f.subModifiers(M_PRIVATE); } if (f.isStatic()) { env.error(f.getWhere(), "abstract.static.modifier", f); f.subModifiers(M_STATIC); } if (f.isFinal()) { env.error(f.getWhere(), "abstract.final.modifier", f); f.subModifiers(M_FINAL); } if (f.isNative()) { env.error(f.getWhere(), "abstract.native.modifier", f); f.subModifiers(M_NATIVE); } if (f.isSynchronized()) { env.error(f.getWhere(),"abstract.synchronized.modifier",f); f.subModifiers(M_SYNCHRONIZED); } } if (f.isAbstract() || f.isNative()) { if (f.getValue() != null) { env.error(f.getWhere(), "invalid.meth.body", f); f.setValue(null); } } else { if (f.getValue() == null) { if (f.isConstructor()) { env.error(f.getWhere(), "no.constructor.body", f); } else { env.error(f.getWhere(), "no.meth.body", f); } f.addModifiers(M_ABSTRACT); } } Vector arguments = f.getArguments(); if (arguments != null) { // arguments can be null if this is an implicit abstract method int argumentLength = arguments.size(); Type argTypes[] = f.getType().getArgumentTypes(); for (int i = 0; i < argTypes.length; i++) { Object arg = arguments.elementAt(i); long where = f.getWhere(); if (arg instanceof MemberDefinition) { where = ((MemberDefinition)arg).getWhere(); arg = ((MemberDefinition)arg).getName(); } // (arg should be an Identifier now) if (argTypes[i].isType(TC_VOID) || argTypes[i].isVoidArray()) { env.error(where, "void.argument", arg); } } } } else if (f.isInnerClass()) { if (f.isVolatile() || f.isTransient() || f.isNative() || f.isSynchronized()) { env.error(f.getWhere(), "inner.modifier", f); f.subModifiers(M_VOLATILE | M_TRANSIENT | M_NATIVE | M_SYNCHRONIZED); } // same check as for fields, below: if (f.getClassDefinition().isInterface() && (f.isPrivate() || f.isProtected())) { env.error(f.getWhere(), "intf.modifier.field", f); f.subModifiers(M_PRIVATE | M_PROTECTED); f.addModifiers(M_PUBLIC); // Fix up the class itself to agree with // the inner-class member. ClassDefinition c = f.getInnerClass(); c.subModifiers(M_PRIVATE | M_PROTECTED); c.addModifiers(M_PUBLIC); } } else { if (f.getType().isType(TC_VOID) || f.getType().isVoidArray()) { env.error(f.getWhere(), "void.inst.var", f.getName()); // REMIND: set type to error return; } if (f.isSynchronized() || f.isAbstract() || f.isNative()) { env.error(f.getWhere(), "var.modifier", f); f.subModifiers(M_SYNCHRONIZED | M_ABSTRACT | M_NATIVE); } if (f.isStrict()) { env.error(f.getWhere(), "var.floatmodifier", f); f.subModifiers(M_STRICTFP); } if (f.isTransient() && isInterface()) { env.error(f.getWhere(), "transient.modifier", f); f.subModifiers(M_TRANSIENT); } if (f.isVolatile() && (isInterface() || f.isFinal())) { env.error(f.getWhere(), "volatile.modifier", f); f.subModifiers(M_VOLATILE); } if (f.isFinal() && (f.getValue() == null) && isInterface()) { env.error(f.getWhere(), "initializer.needed", f); f.subModifiers(M_FINAL); } if (f.getClassDefinition().isInterface() && (f.isPrivate() || f.isProtected())) { env.error(f.getWhere(), "intf.modifier.field", f); f.subModifiers(M_PRIVATE | M_PROTECTED); f.addModifiers(M_PUBLIC); } } // Do not check for repeated methods here: Types are not yet resolved. if (!f.isInitializer()) { for (MemberDefinition f2 = getFirstMatch(f.getName()); f2 != null; f2 = f2.getNextMatch()) { if (f.isVariable() && f2.isVariable()) { env.error(f.getWhere(), "var.multidef", f, f2); return; } else if (f.isInnerClass() && f2.isInnerClass() && !f.getInnerClass().isLocal() && !f2.getInnerClass().isLocal()) { // Found a duplicate inner-class member. // Duplicate local classes are detected in // 'VarDeclarationStatement.checkDeclaration'. env.error(f.getWhere(), "inner.class.multidef", f); return; } } } super.addMember(env, f); }
Create an environment suitable for checking this class. Make sure the source and imports are set right. Make sure the environment contains no context information. (Actually, throw away env altogether and use toplevelEnv instead.)
/** * Create an environment suitable for checking this class. * Make sure the source and imports are set right. * Make sure the environment contains no context information. * (Actually, throw away env altogether and use toplevelEnv instead.) */
public Environment setupEnv(Environment env) { // In some cases, we go to some trouble to create the 'env' argument // that is discarded. We should remove the 'env' argument entirely // as well as the vestigial code that supports it. See comments on // 'newEnvironment' in 'checkInternal' below. return new Environment(toplevelEnv, this); }
A source class never reports deprecation, since the compiler allows access to deprecated features that are being compiled in the same job.
/** * A source class 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; }
See if the source file of this class is right.
See Also:
  • noteUsedBy.noteUsedBy
/** * See if the source file of this class is right. * @see ClassDefinition#noteUsedBy */
public void noteUsedBy(ClassDefinition ref, long where, Environment env) { // If this class is not public, watch for cross-file references. super.noteUsedBy(ref, where, env); ClassDefinition def = this; while (def.isInnerClass()) { def = def.getOuterClass(); } if (def.isPublic()) { return; // already checked } while (ref.isInnerClass()) { ref = ref.getOuterClass(); } if (def.getSource().equals(ref.getSource())) { return; // intra-file reference } ((SourceClass)def).checkSourceFile(env, where); }
Check this class and all its fields.
/** * Check this class and all its fields. */
public void check(Environment env) throws ClassNotFound { if (tracing) env.dtEnter("SourceClass.check: " + getName()); if (isInsideLocal()) { // An inaccessible class gets checked when the surrounding // block is checked. // QUERY: Should this case ever occur? // What would invoke checking of a local class aside from // checking the surrounding method body? if (tracing) env.dtEvent("SourceClass.check: INSIDE LOCAL " + getOuterClass().getName()); getOuterClass().check(env); } else { if (isInnerClass()) { if (tracing) env.dtEvent("SourceClass.check: INNER CLASS " + getOuterClass().getName()); // Make sure the outer is checked first. ((SourceClass)getOuterClass()).maybeCheck(env); } Vset vset = new Vset(); Context ctx = null; if (tracing) env.dtEvent("SourceClass.check: CHECK INTERNAL " + getName()); vset = checkInternal(setupEnv(env), ctx, vset); // drop vset here } if (tracing) env.dtExit("SourceClass.check: " + getName()); } private void maybeCheck(Environment env) throws ClassNotFound { if (tracing) env.dtEvent("SourceClass.maybeCheck: " + getName()); // Check this class now, if it has not yet been checked. // Cf. Main.compile(). Perhaps this code belongs there somehow. ClassDeclaration c = getClassDeclaration(); if (c.getStatus() == CS_PARSED) { // Set it first to avoid vicious circularity: c.setDefinition(this, CS_CHECKED); check(env); } } private Vset checkInternal(Environment env, Context ctx, Vset vset) throws ClassNotFound { Identifier nm = getClassDeclaration().getName(); if (env.verbose()) { env.output("[checking class " + nm + "]"); } // Save context enclosing class for later access // by 'ClassDefinition.resolveName.' classContext = ctx; // At present, the call to 'newEnvironment' is not needed. // The incoming environment to 'basicCheck' is always passed to // 'setupEnv', which discards it completely. This is also the // only call to 'newEnvironment', which is now apparently dead code. basicCheck(Context.newEnvironment(env, ctx)); // 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... // Much of this code was cribbed from 'checkSupers'. // Part of fix for 4094658. ClassDeclaration sup = getSuperClass(); if (sup != null) { long where = getWhere(); where = IdentifierToken.getWhere(superClassId, where); env.resolveExtendsByName(where, this, sup.getName()); } for (int i = 0 ; i < interfaces.length ; i++) { ClassDeclaration intf = interfaces[i]; long where = getWhere(); // Error localization fails here if interfaces were // elided during error recovery from an invalid one. if (interfaceIds != null && interfaceIds.length == interfaces.length) { where = IdentifierToken.getWhere(interfaceIds[i], where); } env.resolveExtendsByName(where, this, intf.getName()); } // Does the name already exist in an imported package? // See JLS 8.1 for the precise rules. if (!isInnerClass() && !isInsideLocal()) { // Discard package qualification for the import checks. Identifier simpleName = nm.getName(); try { // We want this to throw a ClassNotFound exception Imports imports = toplevelEnv.getImports(); Identifier ID = imports.resolve(env, simpleName); if (ID != getName()) env.error(where, "class.multidef.import", simpleName, ID); } catch (AmbiguousClass e) { // At least one of e.name1 and e.name2 must be different Identifier ID = (e.name1 != getName()) ? e.name1 : e.name2; env.error(where, "class.multidef.import", simpleName, ID); } catch (ClassNotFound e) { // we want this to happen } // Make sure that no package with the same fully qualified // name exists. This is required by JLS 7.1. We only need // to perform this check for top level classes -- it isn't // necessary for inner classes. (bug 4101529) // // This change has been backed out because, on WIN32, it // failed to distinguish between java.awt.event and // java.awt.Event when looking for a directory. We will // add this back in later. // // try { // if (env.getPackage(nm).exists()) { // env.error(where, "class.package.conflict", nm); // } // } catch (java.io.IOException ee) { // env.error(where, "io.exception.package", nm); // } // Make sure it was defined in the right file if (isPublic()) { checkSourceFile(env, getWhere()); } } vset = checkMembers(env, ctx, vset); return vset; } private boolean sourceFileChecked = false;
See if the source file of this class is of the right name.
/** * See if the source file of this class is of the right name. */
public void checkSourceFile(Environment env, long where) { // one error per offending class is sufficient if (sourceFileChecked) return; sourceFileChecked = true; String fname = getName().getName() + ".java"; String src = ((ClassFile)getSource()).getName(); if (!src.equals(fname)) { if (isPublic()) { env.error(where, "public.class.file", this, fname); } else { env.error(where, "warn.package.class.file", this, src, fname); } } } // Set true if superclass (but not necessarily superinterfaces) have // been checked. If the superclass is still unresolved, then an error // message should have been issued, and we assume that no further // resolution is possible. private boolean supersChecked = false;
Overrides 'ClassDefinition.getSuperClass'.
/** * Overrides 'ClassDefinition.getSuperClass'. */
public ClassDeclaration getSuperClass(Environment env) { if (tracing) env.dtEnter("SourceClass.getSuperClass: " + this); // Superclass may fail to be set because of error recovery, // so resolve types here only if 'checkSupers' has not yet // completed its checks on the superclass. // QUERY: Can we eliminate the need to resolve superclasses on demand? // See comments in 'checkSupers' and in 'ClassDefinition.getInnerClass'. if (superClass == null && superClassId != null && !supersChecked) { resolveTypeStructure(env); // We used to report an error here if the superclass was not // resolved. Having moved the call to 'checkSupers' from 'basicCheck' // into 'resolveTypeStructure', the errors reported here should have // already been reported. Furthermore, error recovery can null out // the superclass, which would cause a spurious error from the test here. } if (tracing) env.dtExit("SourceClass.getSuperClass: " + this); return superClass; }
Check that all superclasses and superinterfaces are defined and well formed. Among other checks, verify that the inheritance graph is acyclic. Called from 'resolveTypeStructure'.
/** * Check that all superclasses and superinterfaces are defined and * well formed. Among other checks, verify that the inheritance * graph is acyclic. Called from 'resolveTypeStructure'. */
private void checkSupers(Environment env) throws ClassNotFound { // *** DEBUG *** supersCheckStarted = true; if (tracing) env.dtEnter("SourceClass.checkSupers: " + this); if (isInterface()) { if (isFinal()) { Identifier nm = getClassDeclaration().getName(); env.error(getWhere(), "final.intf", nm); // Interfaces have no superclass. Superinterfaces // are checked below, in code shared with the class case. } } else { // Check superclass. // Call to 'getSuperClass(env)' (note argument) attempts // 'resolveTypeStructure' if superclass has not successfully // been resolved. Since we have just now called 'resolveSupers' // (see our call in 'resolveTypeStructure'), it is not clear // that this can do any good. Why not 'getSuperClass()' here? if (getSuperClass(env) != null) { long where = getWhere(); where = IdentifierToken.getWhere(superClassId, where); try { ClassDefinition def = getSuperClass().getClassDefinition(env); // Resolve superclass and its ancestors. def.resolveTypeStructure(env); // Access to the superclass should be checked relative // to the surrounding context, not as if the reference // appeared within the class body. Changed 'canAccess' // to 'extendsCanAccess' to fix 4087314. if (!extendsCanAccess(env, getSuperClass())) { env.error(where, "cant.access.class", getSuperClass()); // Might it be a better recovery to let the access go through? superClass = null; } else if (def.isFinal()) { env.error(where, "super.is.final", getSuperClass()); // Might it be a better recovery to let the access go through? superClass = null; } else if (def.isInterface()) { env.error(where, "super.is.intf", getSuperClass()); superClass = null; } else if (superClassOf(env, getSuperClass())) { env.error(where, "cyclic.super"); superClass = null; } else { def.noteUsedBy(this, where, env); } if (superClass == null) { def = null; } else { // If we have a valid superclass, check its // supers as well, and so on up to root class. // Call to 'enclosingClassOf' will raise // 'NullPointerException' if 'def' is null, // so omit this check as error recovery. ClassDefinition sup = def; for (;;) { if (enclosingClassOf(sup)) { // Do we need a similar test for // interfaces? See bugid 4038529. env.error(where, "super.is.inner"); superClass = null; break; } // Since we resolved the superclass and its // ancestors above, we should not discover // any unresolved classes on the superclass // chain. It should thus be sufficient to // call 'getSuperClass()' (no argument) here. ClassDeclaration s = sup.getSuperClass(env); if (s == null) { // Superclass not resolved due to error. break; } sup = s.getClassDefinition(env); } } } catch (ClassNotFound e) { // Error is detected in call to 'getClassDefinition'. // The class may actually exist but be ambiguous. // Call env.resolve(e.name) to see if it is. // env.resolve(name) will definitely tell us if the // class is ambiguous, but may not necessarily tell // us if the class is not found. // (part of solution for 4059855) reportError: { try { env.resolve(e.name); } catch (AmbiguousClass ee) { env.error(where, "ambig.class", ee.name1, ee.name2); superClass = null; break reportError; } catch (ClassNotFound ee) { // fall through } env.error(where, "super.not.found", e.name, this); superClass = null; } // The break exits this block } } else { // Superclass was null on entry, after call to // 'resolveSupers'. This should normally not happen, // as 'resolveSupers' sets 'superClass' to a non-null // value for all named classes, except for one special // case: 'java.lang.Object', which has no superclass. if (isAnonymous()) { // checker should have filled it in first throw new CompilerError("anonymous super"); } else if (!getName().equals(idJavaLangObject)) { throw new CompilerError("unresolved super"); } } } // At this point, if 'superClass' is null due to an error // in the user program, a message should have been issued. supersChecked = true; // Check interfaces for (int i = 0 ; i < interfaces.length ; i++) { ClassDeclaration intf = interfaces[i]; long where = getWhere(); if (interfaceIds != null && interfaceIds.length == interfaces.length) { where = IdentifierToken.getWhere(interfaceIds[i], where); } try { ClassDefinition def = intf.getClassDefinition(env); // Resolve superinterface and its ancestors. def.resolveTypeStructure(env); // Check superinterface access in the correct context. // Changed 'canAccess' to 'extendsCanAccess' to fix 4087314. if (!extendsCanAccess(env, intf)) { env.error(where, "cant.access.class", intf); } else if (!intf.getClassDefinition(env).isInterface()) { env.error(where, "not.intf", intf); } else if (isInterface() && implementedBy(env, intf)) { env.error(where, "cyclic.intf", intf); } else { def.noteUsedBy(this, where, env); // Interface is OK, leave it in the interface list. continue; } } catch (ClassNotFound e) { // The interface may actually exist but be ambiguous. // Call env.resolve(e.name) to see if it is. // env.resolve(name) will definitely tell us if the // interface is ambiguous, but may not necessarily tell // us if the interface is not found. // (part of solution for 4059855) reportError2: { try { env.resolve(e.name); } catch (AmbiguousClass ee) { env.error(where, "ambig.class", ee.name1, ee.name2); superClass = null; break reportError2; } catch (ClassNotFound ee) { // fall through } env.error(where, "intf.not.found", e.name, this); superClass = null; } // The break exits this block } // Remove this interface from the list of interfaces // as recovery from an error. ClassDeclaration newInterfaces[] = new ClassDeclaration[interfaces.length - 1]; System.arraycopy(interfaces, 0, newInterfaces, 0, i); System.arraycopy(interfaces, i + 1, newInterfaces, i, newInterfaces.length - i); interfaces = newInterfaces; --i; } if (tracing) env.dtExit("SourceClass.checkSupers: " + this); }
Check all of the members of this class.

Inner classes are checked in the following way. Any class which is immediately contained in a block (anonymous and local classes) is checked along with its containing method; see the SourceMember.check() method for more information. Member classes of this class are checked immediately after this class, unless this class is insideLocal(), in which case, they are checked with the rest of the members.

/** * Check all of the members of this class. * <p> * Inner classes are checked in the following way. Any class which * is immediately contained in a block (anonymous and local classes) * is checked along with its containing method; see the * SourceMember.check() method for more information. Member classes * of this class are checked immediately after this class, unless this * class is insideLocal(), in which case, they are checked with the * rest of the members. */
private Vset checkMembers(Environment env, Context ctx, Vset vset) throws ClassNotFound { // bail out if there were any errors if (getError()) { return vset; } // Make sure that all of our member classes have been // basicCheck'ed before we check the rest of our members. // If our member classes haven't been basicCheck'ed, then they // may not have <init> methods. It is important that they // have <init> methods so we can process NewInstanceExpressions // correctly. This problem didn't occur before 1.2beta1. // This is a fix for bug 4082816. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { // System.out.println("Considering " + f + " in " + this); SourceClass cdef = (SourceClass) f.getInnerClass(); if (cdef.isMember()) { cdef.basicCheck(env); } } } if (isFinal() && isAbstract()) { env.error(where, "final.abstract", this.getName().getName()); } // This class should be abstract if there are any abstract methods // in our parent classes and interfaces which we do not override. // There are odd cases when, even though we cannot access some // abstract method from our superclass, that abstract method can // still force this class to be abstract. See the discussion in // bug id 1240831. if (!isInterface() && !isAbstract() && mustBeAbstract(env)) { // Set the class abstract. modifiers |= M_ABSTRACT; // Tell the user which methods force this class to be abstract. // First list all of the "unimplementable" abstract methods. Iterator iter = getPermanentlyAbstractMethods(); while (iter.hasNext()) { MemberDefinition method = (MemberDefinition) iter.next(); // We couldn't override this method even if we // wanted to. Try to make the error message // as non-confusing as possible. env.error(where, "abstract.class.cannot.override", getClassDeclaration(), method, method.getDefiningClassDeclaration()); } // Now list all of the traditional abstract methods. iter = getMethods(env); while (iter.hasNext()) { // For each method, check if it is abstract. If it is, // output an appropriate error message. MemberDefinition method = (MemberDefinition) iter.next(); if (method.isAbstract()) { env.error(where, "abstract.class", getClassDeclaration(), method, method.getDefiningClassDeclaration()); } } } // Check the instance variables in a pre-pass before any constructors. // This lets constructors "in-line" any initializers directly. // It also lets us do some definite assignment checks on variables. Context ctxInit = new Context(ctx); Vset vsInst = vset.copy(); Vset vsClass = vset.copy(); // Do definite assignment checking on blank finals. // Other variables do not need such checks. The simple textual // ordering constraints implemented by MemberDefinition.canReach() // are necessary and sufficient for the other variables. // Note that within non-static code, all statics are always // definitely assigned, and vice-versa. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isVariable() && f.isBlankFinal()) { // The following allocates a LocalMember object as a proxy // to represent the field. int number = ctxInit.declareFieldNumber(f); if (f.isStatic()) { vsClass = vsClass.addVarUnassigned(number); vsInst = vsInst.addVar(number); } else { vsInst = vsInst.addVarUnassigned(number); vsClass = vsClass.addVar(number); } } } // For instance variable checks, use a context with a "this" parameter. Context ctxInst = new Context(ctxInit, this); LocalMember thisArg = getThisArgument(); int thisNumber = ctxInst.declare(env, thisArg); vsInst = vsInst.addVar(thisNumber); // Do all the initializers in order, checking the definite // assignment of blank finals. Separate static from non-static. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { try { if (f.isVariable() || f.isInitializer()) { if (f.isStatic()) { vsClass = f.check(env, ctxInit, vsClass); } else { vsInst = f.check(env, ctxInst, vsInst); } } } catch (ClassNotFound ee) { env.error(f.getWhere(), "class.not.found", ee.name, this); } } checkBlankFinals(env, ctxInit, vsClass, true); // Check the rest of the field definitions. // (Note: Re-checking a field is a no-op.) for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { try { if (f.isConstructor()) { // When checking a constructor, an explicit call to // 'this(...)' makes all blank finals definitely assigned. // See 'MethodExpression.checkValue'. Vset vsCon = f.check(env, ctxInit, vsInst.copy()); // May issue multiple messages for the same variable!! checkBlankFinals(env, ctxInit, vsCon, false); // (drop vsCon here) } else { Vset vsFld = f.check(env, ctx, vset.copy()); // (drop vsFld here) } } catch (ClassNotFound ee) { env.error(f.getWhere(), "class.not.found", ee.name, this); } } // Must mark class as checked before visiting inner classes, // as they may in turn request checking of the current class // as an outer class. Fix for bug id 4056774. getClassDeclaration().setDefinition(this, CS_CHECKED); // Also check other classes in the same nest. // All checking of this nest must be finished before any // of its classes emit bytecode. // Otherwise, the inner classes might not have a chance to // add access or class literal fields to the outer class. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass cdef = (SourceClass) f.getInnerClass(); if (!cdef.isInsideLocal()) { cdef.maybeCheck(env); } } } // Note: Since inner classes cannot set up-level variables, // the returned vset is always equal to the passed-in vset. // Still, we'll return it for the sake of regularity. return vset; }
Make sure all my blank finals exist now.
/** Make sure all my blank finals exist now. */
private void checkBlankFinals(Environment env, Context ctxInit, Vset vset, boolean isStatic) { for (int i = 0; i < ctxInit.getVarNumber(); i++) { if (!vset.testVar(i)) { MemberDefinition ff = ctxInit.getElement(i); if (ff != null && ff.isBlankFinal() && ff.isStatic() == isStatic && ff.getClassDefinition() == this) { env.error(ff.getWhere(), "final.var.not.initialized", ff.getName()); } } } }
Check this class has its superclass and its interfaces. Also force it to have an method (if it doesn't already have one) and to have all the abstract methods of its parents.
/** * Check this class has its superclass and its interfaces. Also * force it to have an <init> method (if it doesn't already have one) * and to have all the abstract methods of its parents. */
private boolean basicChecking = false; private boolean basicCheckDone = false; protected void basicCheck(Environment env) throws ClassNotFound { if (tracing) env.dtEnter("SourceClass.basicCheck: " + getName()); super.basicCheck(env); if (basicChecking || basicCheckDone) { if (tracing) env.dtExit("SourceClass.basicCheck: OK " + getName()); return; } if (tracing) env.dtEvent("SourceClass.basicCheck: CHECKING " + getName()); basicChecking = true; env = setupEnv(env); Imports imports = env.getImports(); if (imports != null) { imports.resolve(env); } resolveTypeStructure(env); // Check the existence of the superclass and all interfaces. // Also responsible for breaking inheritance cycles. This call // has been moved to 'resolveTypeStructure', just after the call // to 'resolveSupers', as inheritance cycles must be broken before // resolving types within the members. Fixes 4073739. // checkSupers(env); if (!isInterface()) { // Add implicit <init> method, if necessary. // QUERY: What keeps us from adding an implicit constructor // when the user explicitly declares one? Is it truly guaranteed // that the declaration for such an explicit constructor will have // been processed by the time we arrive here? In general, 'basicCheck' // is called very early, prior to the normal member checking phase. if (!hasConstructor()) { Node code = new CompoundStatement(getWhere(), new Statement[0]); Type t = Type.tMethod(Type.tVoid); // Default constructors inherit the access modifiers of their // class. For non-inner classes, this follows from JLS 8.6.7, // as the only possible modifier is 'public'. For the sake of // robustness in the presence of errors, we ignore any other // modifiers. For inner classes, the rule needs to be extended // in some way to account for the possibility of private and // protected classes. We make the 'obvious' extension, however, // the inner classes spec is silent on this issue, and a definitive // resolution is needed. See bugid 4087421. // WORKAROUND: A private constructor might need an access method, // but it is not possible to create one due to a restriction in // the verifier. (This is a known problem -- see 4015397.) // We therefore do not inherit the 'private' modifier from the class, // allowing the default constructor to be package private. This // workaround can be observed via reflection, but is otherwise // undetectable, as the constructor is always accessible within // the class in which its containing (private) class appears. int accessModifiers = getModifiers() & (isInnerClass() ? (M_PUBLIC | M_PROTECTED) : M_PUBLIC); env.makeMemberDefinition(env, getWhere(), this, null, accessModifiers, t, idInit, null, null, code); } } // Only do the inheritance/override checks if they are turned on. // The idea here is that they will be done in javac, but not // in javadoc. See the comment for turnOffChecks(), above. if (doInheritanceChecks) { // Verify the compatibility of all inherited method definitions // by collecting all of our inheritable methods. collectInheritedMethods(env); } basicChecking = false; basicCheckDone = true; if (tracing) env.dtExit("SourceClass.basicCheck: " + getName()); }
Add a group of methods to this class as miranda methods. For a definition of Miranda methods, see the comment above the method addMirandaMethods() in the file sun/tools/java/ClassDeclaration.java
/** * Add a group of methods to this class as miranda methods. * * For a definition of Miranda methods, see the comment above the * method addMirandaMethods() in the file * sun/tools/java/ClassDeclaration.java */
protected void addMirandaMethods(Environment env, Iterator mirandas) { while(mirandas.hasNext()) { MemberDefinition method = (MemberDefinition)mirandas.next(); addMember(method); //System.out.println("adding miranda method " + newMethod + // " to " + this); } }
After parsing is complete, resolve all names except those inside method bodies or initializers. In particular, this is the point at which we find out what kinds of variables and methods there are in the classes, and therefore what is each class's interface to the world.

Also perform certain other transformations, such as inserting "this$C" arguments into constructors, and reorganizing structure to flatten qualified member names.

Do not perform type-based or name-based consistency checks or normalizations (such as default nullary constructors), and do not attempt to compile code against this class, until after this phase.

/** * <em>After parsing is complete</em>, resolve all names * except those inside method bodies or initializers. * In particular, this is the point at which we find out what * kinds of variables and methods there are in the classes, * and therefore what is each class's interface to the world. * <p> * Also perform certain other transformations, such as inserting * "this$C" arguments into constructors, and reorganizing structure * to flatten qualified member names. * <p> * Do not perform type-based or name-based consistency checks * or normalizations (such as default nullary constructors), * and do not attempt to compile code against this class, * until after this phase. */
private boolean resolving = false; public void resolveTypeStructure(Environment env) { if (tracing) env.dtEnter("SourceClass.resolveTypeStructure: " + getName()); // Resolve immediately enclosing type, which in turn // forces resolution of all enclosing type declarations. ClassDefinition oc = getOuterClass(); if (oc != null && oc instanceof SourceClass && !((SourceClass)oc).resolved) { // Do the outer class first, always. ((SourceClass)oc).resolveTypeStructure(env); // (Note: this.resolved is probably true at this point.) } // Punt if we've already resolved this class, or are currently // in the process of doing so. if (resolved || resolving) { if (tracing) env.dtExit("SourceClass.resolveTypeStructure: OK " + getName()); return; } // Previously, 'resolved' was set here, and served to prevent // duplicate resolutions here as well as its function in // 'ClassDefinition.addMember'. Now, 'resolving' serves the // former purpose, distinct from that of 'resolved'. resolving = true; if (tracing) env.dtEvent("SourceClass.resolveTypeStructure: RESOLVING " + getName()); env = setupEnv(env); // Resolve superclass names to class declarations // for the immediate superclass and superinterfaces. resolveSupers(env); // Check all ancestor superclasses for various // errors, verifying definition of all superclasses // and superinterfaces. Also breaks inheritance cycles. // Calls 'resolveTypeStructure' recursively for ancestors // This call used to appear in 'basicCheck', but was not // performed early enough. Most of the compiler will barf // on inheritance cycles! try { checkSupers(env); } catch (ClassNotFound ee) { // Undefined classes should be reported by 'checkSupers'. env.error(where, "class.not.found", ee.name, this); } for (MemberDefinition f = getFirstMember() ; f != null ; f = f.getNextMember()) { if (f instanceof SourceMember) ((SourceMember)f).resolveTypeStructure(env); } resolving = false; // Mark class as resolved. If new members are subsequently // added to the class, they will be resolved at that time. // See 'ClassDefinition.addMember'. Previously, this variable was // set prior to the calls to 'checkSupers' and 'resolveTypeStructure' // (which may engender further calls to 'checkSupers'). This could // lead to duplicate resolution of implicit constructors, as the call to // 'basicCheck' from 'checkSupers' could add the constructor while // its class is marked resolved, and thus would resolve the constructor, // believing it to be a "late addition". It would then be resolved // redundantly during the normal traversal of the members, which // immediately follows in the code above. resolved = true; // Now we have enough information to detect method repeats. for (MemberDefinition f = getFirstMember() ; f != null ; f = f.getNextMember()) { if (f.isInitializer()) continue; if (!f.isMethod()) continue; for (MemberDefinition f2 = f; (f2 = f2.getNextMatch()) != null; ) { if (!f2.isMethod()) continue; if (f.getType().equals(f2.getType())) { env.error(f.getWhere(), "meth.multidef", f); continue; } if (f.getType().equalArguments(f2.getType())) { env.error(f.getWhere(), "meth.redef.rettype", f, f2); continue; } } } if (tracing) env.dtExit("SourceClass.resolveTypeStructure: " + getName()); } protected void resolveSupers(Environment env) { if (tracing) env.dtEnter("SourceClass.resolveSupers: " + this); // Find the super class if (superClassId != null && superClass == null) { superClass = resolveSuper(env, superClassId); // Special-case java.lang.Object here (not in the parser). // In all other cases, if we have a valid 'superClassId', // we return with a valid and non-null 'superClass' value. if (superClass == getClassDeclaration() && getName().equals(idJavaLangObject)) { superClass = null; superClassId = null; } } // Find interfaces if (interfaceIds != null && interfaces == null) { interfaces = new ClassDeclaration[interfaceIds.length]; for (int i = 0 ; i < interfaces.length ; i++) { interfaces[i] = resolveSuper(env, interfaceIds[i]); for (int j = 0; j < i; j++) { if (interfaces[i] == interfaces[j]) { Identifier id = interfaceIds[i].getName(); long where = interfaceIds[j].getWhere(); env.error(where, "intf.repeated", id); } } } } if (tracing) env.dtExit("SourceClass.resolveSupers: " + this); } private ClassDeclaration resolveSuper(Environment env, IdentifierToken t) { Identifier name = t.getName(); if (tracing) env.dtEnter("SourceClass.resolveSuper: " + name); if (isInnerClass()) name = outerClass.resolveName(env, name); else name = env.resolveName(name); ClassDeclaration result = env.getClassDeclaration(name); // Result is never null, as a new 'ClassDeclaration' is // created if one with the given name does not exist. if (tracing) env.dtExit("SourceClass.resolveSuper: " + name); return result; }
During the type-checking of an outer method body or initializer, this routine is called to check a local class body in the proper context.
Params:
  • sup – the named super class or interface (if anonymous)
  • args – the actual arguments (if anonymous)
/** * During the type-checking of an outer method body or initializer, * this routine is called to check a local class body * in the proper context. * @param sup the named super class or interface (if anonymous) * @param args the actual arguments (if anonymous) */
public Vset checkLocalClass(Environment env, Context ctx, Vset vset, ClassDefinition sup, Expression args[], Type argTypes[] ) throws ClassNotFound { env = setupEnv(env); if ((sup != null) != isAnonymous()) { throw new CompilerError("resolveAnonymousStructure"); } if (isAnonymous()) { resolveAnonymousStructure(env, sup, args, argTypes); } // Run the checks in the lexical context from the outer class. vset = checkInternal(env, ctx, vset); // This is now done by 'checkInternal' via its call to 'checkMembers'. // getClassDeclaration().setDefinition(this, CS_CHECKED); return vset; }
As with checkLocalClass, run the inline phase for a local class.
/** * As with checkLocalClass, run the inline phase for a local class. */
public void inlineLocalClass(Environment env) { for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if ((f.isVariable() || f.isInitializer()) && !f.isStatic()) { continue; // inlined inside of constructors only } try { ((SourceMember)f).inline(env); } catch (ClassNotFound ee) { env.error(f.getWhere(), "class.not.found", ee.name, this); } } if (getReferencesFrozen() != null && !inlinedLocalClass) { inlinedLocalClass = true; // add more constructor arguments for uplevel references for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isConstructor()) { //((SourceMember)f).addUplevelArguments(false); ((SourceMember)f).addUplevelArguments(); } } } } private boolean inlinedLocalClass = false;
Check a class which is inside a local class, but is not itself local.
/** * Check a class which is inside a local class, but is not itself local. */
public Vset checkInsideClass(Environment env, Context ctx, Vset vset) throws ClassNotFound { if (!isInsideLocal() || isLocal()) { throw new CompilerError("checkInsideClass"); } return checkInternal(env, ctx, vset); }
Just before checking an anonymous class, decide its true inheritance, and build its (sole, implicit) constructor.
/** * Just before checking an anonymous class, decide its true * inheritance, and build its (sole, implicit) constructor. */
private void resolveAnonymousStructure(Environment env, ClassDefinition sup, Expression args[], Type argTypes[] ) throws ClassNotFound { if (tracing) env.dtEvent("SourceClass.resolveAnonymousStructure: " + this + ", super " + sup); // Decide now on the superclass. // This check has been removed as part of the fix for 4055017. // In the anonymous class created to hold the 'class$' method // of an interface, 'superClassId' refers to 'java.lang.Object'. /*---------------------* if (!(superClass == null && superClassId.getName() == idNull)) { throw new CompilerError("superclass "+superClass); } *---------------------*/ if (sup.isInterface()) { // allow an interface in the "super class" position int ni = (interfaces == null) ? 0 : interfaces.length; ClassDeclaration i1[] = new ClassDeclaration[1+ni]; if (ni > 0) { System.arraycopy(interfaces, 0, i1, 1, ni); if (interfaceIds != null && interfaceIds.length == ni) { IdentifierToken id1[] = new IdentifierToken[1+ni]; System.arraycopy(interfaceIds, 0, id1, 1, ni); id1[0] = new IdentifierToken(sup.getName()); } } i1[0] = sup.getClassDeclaration(); interfaces = i1; sup = toplevelEnv.getClassDefinition(idJavaLangObject); } superClass = sup.getClassDeclaration(); if (hasConstructor()) { throw new CompilerError("anonymous constructor"); } // Synthesize an appropriate constructor. Type t = Type.tMethod(Type.tVoid, argTypes); IdentifierToken names[] = new IdentifierToken[argTypes.length]; for (int i = 0; i < names.length; i++) { names[i] = new IdentifierToken(args[i].getWhere(), Identifier.lookup("$"+i)); } int outerArg = (sup.isTopLevel() || sup.isLocal()) ? 0 : 1; Expression superArgs[] = new Expression[-outerArg + args.length]; for (int i = outerArg ; i < args.length ; i++) { superArgs[-outerArg + i] = new IdentifierExpression(names[i]); } long where = getWhere(); Expression superExp; if (outerArg == 0) { superExp = new SuperExpression(where); } else { superExp = new SuperExpression(where, new IdentifierExpression(names[0])); } Expression superCall = new MethodExpression(where, superExp, idInit, superArgs); Statement body[] = { new ExpressionStatement(where, superCall) }; Node code = new CompoundStatement(where, body); int mod = M_SYNTHETIC; // ISSUE: make M_PRIVATE, with wrapper? env.makeMemberDefinition(env, where, this, null, mod, t, idInit, names, null, code); }
Convert class modifiers to a string for diagnostic purposes. Accepts modifiers applicable to inner classes and that appear in the InnerClasses attribute only, as well as those that may appear in the class modifier proper.
/** * Convert class modifiers to a string for diagnostic purposes. * Accepts modifiers applicable to inner classes and that appear * in the InnerClasses attribute only, as well as those that may * appear in the class modifier proper. */
private static int classModifierBits[] = { ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_INTERFACE, ACC_ABSTRACT, ACC_SUPER, M_ANONYMOUS, M_LOCAL, M_STRICTFP, ACC_STRICT}; private static String classModifierNames[] = { "PUBLIC", "PRIVATE", "PROTECTED", "STATIC", "FINAL", "INTERFACE", "ABSTRACT", "SUPER", "ANONYMOUS", "LOCAL", "STRICTFP", "STRICT"}; static String classModifierString(int mods) { String s = ""; for (int i = 0; i < classModifierBits.length; i++) { if ((mods & classModifierBits[i]) != 0) { s = s + " " + classModifierNames[i]; mods &= ~classModifierBits[i]; } } if (mods != 0) { s = s + " ILLEGAL:" + Integer.toHexString(mods); } return s; }
Find or create an access method for a private member, or return null if this is not possible.
/** * Find or create an access method for a private member, * or return null if this is not possible. */
public MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { return getAccessMember(env, ctx, field, false, isSuper); } public MemberDefinition getUpdateMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { if (!field.isVariable()) { throw new CompilerError("method"); } return getAccessMember(env, ctx, field, true, isSuper); } private MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isUpdate, boolean isSuper) { // The 'isSuper' argument is really only meaningful when the // target member is a method, in which case an 'invokespecial' // is needed. For fields, 'getfield' and 'putfield' instructions // are generated in either case, and 'isSuper' currently plays // no essential role. Nonetheless, we maintain the distinction // consistently for the time being. boolean isStatic = field.isStatic(); boolean isMethod = field.isMethod(); // Find pre-existing access method. // In the case of a field access method, we only look for the getter. // A getter is always created whenever a setter is. // QUERY: Why doesn't the 'MemberDefinition' object for the field // itself just have fields for its getter and setter? MemberDefinition af; for (af = getFirstMember(); af != null; af = af.getNextMember()) { if (af.getAccessMethodTarget() == field) { if (isMethod && af.isSuperAccessMethod() == isSuper) { break; } // Distinguish the getter and the setter by the number of // arguments. int nargs = af.getType().getArgumentTypes().length; // This was (nargs == (isStatic ? 0 : 1) + (isUpdate ? 1 : 0)) // in order to find a setter as well as a getter. This caused // allocation of multiple getters. if (nargs == (isStatic ? 0 : 1)) { break; } } } if (af != null) { if (!isUpdate) { return af; } else { MemberDefinition uf = af.getAccessUpdateMember(); if (uf != null) { return uf; } } } else if (isUpdate) { // must find or create the getter before creating the setter af = getAccessMember(env, ctx, field, false, isSuper); } // If we arrive here, we are creating a new access member. Identifier anm; Type dummyType = null; if (field.isConstructor()) { // For a constructor, we use the same name as for all // constructors ("<init>"), but add a distinguishing // argument of an otherwise unused "dummy" type. anm = idInit; // Get the dummy class, creating it if necessary. SourceClass outerMostClass = (SourceClass)getTopClass(); dummyType = outerMostClass.dummyArgumentType; if (dummyType == null) { // Create dummy class. IdentifierToken sup = new IdentifierToken(0, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(0, idNull); int mod = M_ANONYMOUS | M_STATIC | M_SYNTHETIC; // If an interface has a public inner class, the dummy class for // the constructor must always be accessible. Fix for 4221648. if (outerMostClass.isInterface()) { mod |= M_PUBLIC; } ClassDefinition dummyClass = toplevelEnv.makeClassDefinition(toplevelEnv, 0, t, null, mod, sup, interfaces, outerMostClass); // Check the class. // It is likely that a full check is not really necessary, // but it is essential that the class be marked as parsed. dummyClass.getClassDeclaration().setDefinition(dummyClass, CS_PARSED); Expression argsX[] = {}; Type argTypesX[] = {}; try { ClassDefinition supcls = toplevelEnv.getClassDefinition(idJavaLangObject); dummyClass.checkLocalClass(toplevelEnv, null, new Vset(), supcls, argsX, argTypesX); } catch (ClassNotFound ee) {}; // Get class type. dummyType = dummyClass.getType(); outerMostClass.dummyArgumentType = dummyType; } } else { // Otherwise, we use the name "access$N", for the // smallest value of N >= 0 yielding an unused name. for (int i = 0; ; i++) { anm = Identifier.lookup(prefixAccess + i); if (getFirstMatch(anm) == null) { break; } } } Type argTypes[]; Type t = field.getType(); if (isStatic) { if (!isMethod) { if (!isUpdate) { Type at[] = { }; argTypes = at; t = Type.tMethod(t); // nullary getter } else { Type at[] = { t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Since constructors are never static, we don't // have to worry about a dummy argument here. argTypes = t.getArgumentTypes(); } } else { // All access methods for non-static members get an explicit // 'this' pointer as an extra argument, as the access methods // themselves must be static. EXCEPTION: Access methods for // constructors are non-static. Type classType = this.getType(); if (!isMethod) { if (!isUpdate) { Type at[] = { classType }; argTypes = at; t = Type.tMethod(t, argTypes); // nullary getter } else { Type at[] = { classType, t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Target is a method, possibly a constructor. Type at[] = t.getArgumentTypes(); int nargs = at.length; if (field.isConstructor()) { // Access method is a constructor. // Requires a dummy argument. MemberDefinition outerThisArg = ((SourceMember)field).getOuterThisArg(); if (outerThisArg != null) { // Outer instance link must be the first argument. // The following is a sanity check that will catch // most cases in which in this requirement is violated. if (at[0] != outerThisArg.getType()) { throw new CompilerError("misplaced outer this"); } // Strip outer 'this' argument. // It will be added back when the access method is checked. argTypes = new Type[nargs]; argTypes[0] = dummyType; for (int i = 1; i < nargs; i++) { argTypes[i] = at[i]; } } else { // There is no outer instance. argTypes = new Type[nargs+1]; argTypes[0] = dummyType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } } else { // Access method is static. // Requires an explicit 'this' argument. argTypes = new Type[nargs+1]; argTypes[0] = classType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } t = Type.tMethod(t.getReturnType(), argTypes); } } int nlen = argTypes.length; long where = field.getWhere(); IdentifierToken names[] = new IdentifierToken[nlen]; for (int i = 0; i < nlen; i++) { names[i] = new IdentifierToken(where, Identifier.lookup("$"+i)); } Expression access = null; Expression thisArg = null; Expression args[] = null; if (isStatic) { args = new Expression[nlen]; for (int i = 0 ; i < nlen ; i++) { args[i] = new IdentifierExpression(names[i]); } } else { if (field.isConstructor()) { // Constructor access method is non-static, so // 'this' works normally. thisArg = new ThisExpression(where); // Remove dummy argument, as it is not // passed to the target method. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } else { // Non-constructor access method is static, so // we use the first argument as 'this'. thisArg = new IdentifierExpression(names[0]); // Remove first argument. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } access = thisArg; } if (!isMethod) { access = new FieldExpression(where, access, field); if (isUpdate) { access = new AssignExpression(where, access, args[0]); } } else { // If true, 'isSuper' forces a non-virtual call. access = new MethodExpression(where, access, field, args, isSuper); } Statement code; if (t.getReturnType().isType(TC_VOID)) { code = new ExpressionStatement(where, access); } else { code = new ReturnStatement(where, access); } Statement body[] = { code }; code = new CompoundStatement(where, body); // Access methods are now static (constructors excepted), and no longer final. // This change was mandated by the interaction of the access method // naming conventions and the restriction against overriding final // methods. int mod = M_SYNTHETIC; if (!field.isConstructor()) { mod |= M_STATIC; } // Create the synthetic method within the class in which the referenced // private member appears. The 'env' argument to 'makeMemberDefinition' // is suspect because it represents the environment at the point at // which a reference takes place, while it should represent the // environment in which the definition of the synthetic method appears. // We get away with this because 'env' is used only to access globals // such as 'Environment.error', and also as an argument to // 'resolveTypeStructure', which immediately discards it using // 'setupEnv'. Apparently, the current definition of 'setupEnv' // represents a design change that has not been thoroughly propagated. // An access method is declared with same list of exceptions as its // target. As the exceptions are simply listed by name, the correctness // of this approach requires that the access method be checked // (name-resolved) in the same context as its target method This // should always be the case. SourceMember newf = (SourceMember) env.makeMemberDefinition(env, where, this, null, mod, t, anm, names, field.getExceptionIds(), code); // Just to be safe, copy over the name-resolved exceptions from the // target so that the context in which the access method is checked // doesn't matter. newf.setExceptions(field.getExceptions(env)); newf.setAccessMethodTarget(field); if (isUpdate) { af.setAccessUpdateMember(newf); } newf.setIsSuperAccessMethod(isSuper); // The call to 'check' is not needed, as the access method will be // checked by the containing class after it is added. This is the // idiom followed in the implementation of class literals. (See // 'FieldExpression.java'.) In any case, the context is wrong in the // call below. The access method must be checked in the context in // which it is declared, i.e., the class containing the referenced // private member, not the (inner) class in which the original member // reference occurs. // // try { // newf.check(env, ctx, new Vset()); // } catch (ClassNotFound ee) { // env.error(where, "class.not.found", ee.name, this); // } // The comment above is inaccurate. While it is often the case // that the containing class will check the access method, this is // by no means guaranteed. In fact, an access method may be added // after the checking of its class is complete. In this case, however, // the context in which the class was checked will have been saved in // the class definition object (by the fix for 4095716), allowing us // to check the field now, and in the correct context. // This fixes bug 4098093. Context checkContext = newf.getClassDefinition().getClassContext(); if (checkContext != null) { //System.out.println("checking late addition: " + this); try { newf.check(env, checkContext, new Vset()); } catch (ClassNotFound ee) { env.error(where, "class.not.found", ee.name, this); } } //System.out.println("[Access member '" + // newf + "' created for field '" + // field +"' in class '" + this + "']"); return newf; }
Find an inner class of 'this', chosen arbitrarily. Result is always an actual class, never an interface. Returns null if none found.
/** * Find an inner class of 'this', chosen arbitrarily. * Result is always an actual class, never an interface. * Returns null if none found. */
SourceClass findLookupContext() { // Look for an immediate inner class. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass ic = (SourceClass)f.getInnerClass(); if (!ic.isInterface()) { return ic; } } } // Look for a class nested within an immediate inner interface. // At this point, we have given up on finding a minimally-nested // class (which would require a breadth-first traversal). It doesn't // really matter which inner class we find. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass lc = ((SourceClass)f.getInnerClass()).findLookupContext(); if (lc != null) { return lc; } } } // No inner classes. return null; } private MemberDefinition lookup = null;
Get helper method for class literal lookup.
/** * Get helper method for class literal lookup. */
public MemberDefinition getClassLiteralLookup(long fwhere) { // If we have already created a lookup method, reuse it. if (lookup != null) { return lookup; } // If the current class is a nested class, make sure we put the // lookup method in the outermost class. Set 'lookup' for the // intervening inner classes so we won't have to do the search // again. if (outerClass != null) { lookup = outerClass.getClassLiteralLookup(fwhere); return lookup; } // If we arrive here, there was no existing 'class$' method. ClassDefinition c = this; boolean needNewClass = false; if (isInterface()) { // The top-level type is an interface. Try to find an existing // inner class in which to create the helper method. Any will do. c = findLookupContext(); if (c == null) { // The interface has no inner classes. Create an anonymous // inner class to hold the helper method, as an interface must // not have any methods. The tests above for prior creation // of a 'class$' method assure that only one such class is // allocated for each outermost class containing a class // literal embedded somewhere within. Part of fix for 4055017. needNewClass = true; IdentifierToken sup = new IdentifierToken(fwhere, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(fwhere, idNull); int mod = M_PUBLIC | M_ANONYMOUS | M_STATIC | M_SYNTHETIC; c = (SourceClass) toplevelEnv.makeClassDefinition(toplevelEnv, fwhere, t, null, mod, sup, interfaces, this); } } // The name of the class-getter stub is "class$" Identifier idDClass = Identifier.lookup(prefixClass); Type strarg[] = { Type.tString }; // Some sanity checks of questionable value. // // This check became useless after matchMethod() was modified // to not return synthetic methods. // //try { // lookup = c.matchMethod(toplevelEnv, c, idDClass, strarg); //} catch (ClassNotFound ee) { // throw new CompilerError("unexpected missing class"); //} catch (AmbiguousMember ee) { // throw new CompilerError("synthetic name clash"); //} //if (lookup != null && lookup.getClassDefinition() == c) { // // Error if method found was not inherited. // throw new CompilerError("unexpected duplicate"); //} // Some sanity checks of questionable value. /* // The helper function looks like this. * // It simply maps a checked exception to an unchecked one. * static Class class$(String class$) { * try { return Class.forName(class$); } * catch (ClassNotFoundException forName) { * throw new NoClassDefFoundError(forName.getMessage()); * } * } */ long w = c.getWhere(); IdentifierToken arg = new IdentifierToken(w, idDClass); Expression e = new IdentifierExpression(arg); Expression a1[] = { e }; Identifier idForName = Identifier.lookup("forName"); e = new MethodExpression(w, new TypeExpression(w, Type.tClassDesc), idForName, a1); Statement body = new ReturnStatement(w, e); // map the exceptions Identifier idClassNotFound = Identifier.lookup("java.lang.ClassNotFoundException"); Identifier idNoClassDefFound = Identifier.lookup("java.lang.NoClassDefFoundError"); Type ctyp = Type.tClass(idClassNotFound); Type exptyp = Type.tClass(idNoClassDefFound); Identifier idGetMessage = Identifier.lookup("getMessage"); e = new IdentifierExpression(w, idForName); e = new MethodExpression(w, e, idGetMessage, new Expression[0]); Expression a2[] = { e }; e = new NewInstanceExpression(w, new TypeExpression(w, exptyp), a2); Statement handler = new CatchStatement(w, new TypeExpression(w, ctyp), new IdentifierToken(idForName), new ThrowStatement(w, e)); Statement handlers[] = { handler }; body = new TryStatement(w, body, handlers); Type mtype = Type.tMethod(Type.tClassDesc, strarg); IdentifierToken args[] = { arg }; // Use default (package) access. If private, an access method would // be needed in the event that the class literal belonged to an interface. // Also, making it private tickles bug 4098316. lookup = toplevelEnv.makeMemberDefinition(toplevelEnv, w, c, null, M_STATIC | M_SYNTHETIC, mtype, idDClass, args, null, body); // If a new class was created to contain the helper method, // check it now. if (needNewClass) { if (c.getClassDeclaration().getStatus() == CS_CHECKED) { throw new CompilerError("duplicate check"); } c.getClassDeclaration().setDefinition(c, CS_PARSED); Expression argsX[] = {}; Type argTypesX[] = {}; try { ClassDefinition sup = toplevelEnv.getClassDefinition(idJavaLangObject); c.checkLocalClass(toplevelEnv, null, new Vset(), sup, argsX, argTypesX); } catch (ClassNotFound ee) {}; } return lookup; }
A list of active ongoing compilations. This list is used to stop two compilations from saving the same class.
/** * A list of active ongoing compilations. This list * is used to stop two compilations from saving the * same class. */
private static Vector active = new Vector();
Compile this class
/** * Compile this class */
public void compile(OutputStream out) throws InterruptedException, IOException { Environment env = toplevelEnv; synchronized (active) { while (active.contains(getName())) { active.wait(); } active.addElement(getName()); } try { compileClass(env, out); } catch (ClassNotFound e) { throw new CompilerError(e); } finally { synchronized (active) { active.removeElement(getName()); active.notifyAll(); } } }
Verify that the modifier bits included in 'required' are all present in 'mods', otherwise signal an internal error. Note that errors in the source program may corrupt the modifiers, thus we rely on the fact that 'CompilerError' exceptions are silently ignored after an error message has been issued.
/** * Verify that the modifier bits included in 'required' are * all present in 'mods', otherwise signal an internal error. * Note that errors in the source program may corrupt the modifiers, * thus we rely on the fact that 'CompilerError' exceptions are * silently ignored after an error message has been issued. */
private static void assertModifiers(int mods, int required) { if ((mods & required) != required) { throw new CompilerError("illegal class modifiers"); } } protected void compileClass(Environment env, OutputStream out) throws IOException, ClassNotFound { Vector variables = new Vector(); Vector methods = new Vector(); Vector innerClasses = new Vector(); CompilerMember init = new CompilerMember(new MemberDefinition(getWhere(), this, M_STATIC, Type.tMethod(Type.tVoid), idClassInit, null, null), new Assembler()); Context ctx = new Context((Context)null, init.field); for (ClassDefinition def = this; def.isInnerClass(); def = def.getOuterClass()) { innerClasses.addElement(def); } // Reverse the order, so that outer levels come first: int ncsize = innerClasses.size(); for (int i = ncsize; --i >= 0; ) innerClasses.addElement(innerClasses.elementAt(i)); for (int i = ncsize; --i >= 0; ) innerClasses.removeElementAt(i); // System.out.println("compile class " + getName()); boolean haveDeprecated = this.isDeprecated(); boolean haveSynthetic = this.isSynthetic(); boolean haveConstantValue = false; boolean haveExceptions = false; // Generate code for all fields for (SourceMember field = (SourceMember)getFirstMember(); field != null; field = (SourceMember)field.getNextMember()) { //System.out.println("compile field " + field.getName()); haveDeprecated |= field.isDeprecated(); haveSynthetic |= field.isSynthetic(); try { if (field.isMethod()) { haveExceptions |= (field.getExceptions(env).length > 0); if (field.isInitializer()) { if (field.isStatic()) { field.code(env, init.asm); } } else { CompilerMember f = new CompilerMember(field, new Assembler()); field.code(env, f.asm); methods.addElement(f); } } else if (field.isInnerClass()) { innerClasses.addElement(field.getInnerClass()); } else if (field.isVariable()) { field.inline(env); CompilerMember f = new CompilerMember(field, null); variables.addElement(f); if (field.isStatic()) { field.codeInit(env, ctx, init.asm); } haveConstantValue |= (field.getInitialValue() != null); } } catch (CompilerError ee) { ee.printStackTrace(); env.error(field, 0, "generic", field.getClassDeclaration() + ":" + field + "@" + ee.toString(), null, null); } } if (!init.asm.empty()) { init.asm.add(getWhere(), opc_return, true); methods.addElement(init); } // bail out if there were any errors if (getNestError()) { return; } int nClassAttrs = 0; // Insert constants if (methods.size() > 0) { tab.put("Code"); } if (haveConstantValue) { tab.put("ConstantValue"); } String sourceFile = null; if (env.debug_source()) { sourceFile = ((ClassFile)getSource()).getName(); tab.put("SourceFile"); tab.put(sourceFile); nClassAttrs += 1; } if (haveExceptions) { tab.put("Exceptions"); } if (env.debug_lines()) { tab.put("LineNumberTable"); } if (haveDeprecated) { tab.put("Deprecated"); if (this.isDeprecated()) { nClassAttrs += 1; } } if (haveSynthetic) { tab.put("Synthetic"); if (this.isSynthetic()) { nClassAttrs += 1; } } // JCOV if (env.coverage()) { nClassAttrs += 2; // AbsoluteSourcePath, TimeStamp tab.put("AbsoluteSourcePath"); tab.put("TimeStamp"); tab.put("CoverageTable"); } // end JCOV if (env.debug_vars()) { tab.put("LocalVariableTable"); } if (innerClasses.size() > 0) { tab.put("InnerClasses"); nClassAttrs += 1; // InnerClasses } // JCOV String absoluteSourcePath = ""; long timeStamp = 0; if (env.coverage()) { absoluteSourcePath = getAbsoluteName(); timeStamp = System.currentTimeMillis(); tab.put(absoluteSourcePath); } // end JCOV tab.put(getClassDeclaration()); if (getSuperClass() != null) { tab.put(getSuperClass()); } for (int i = 0 ; i < interfaces.length ; i++) { tab.put(interfaces[i]); } // Sort the methods in order to make sure both constant pool // entries and methods are in a deterministic order from run // to run (this allows comparing class files for a fixed point // to validate the compiler) CompilerMember[] ordered_methods = new CompilerMember[methods.size()]; methods.copyInto(ordered_methods); java.util.Arrays.sort(ordered_methods); for (int i=0; i<methods.size(); i++) methods.setElementAt(ordered_methods[i], i); // Optimize Code and Collect method constants for (Enumeration e = methods.elements() ; e.hasMoreElements() ; ) { CompilerMember f = (CompilerMember)e.nextElement(); try { f.asm.optimize(env); f.asm.collect(env, f.field, tab); tab.put(f.name); tab.put(f.sig); ClassDeclaration exp[] = f.field.getExceptions(env); for (int i = 0 ; i < exp.length ; i++) { tab.put(exp[i]); } } catch (Exception ee) { ee.printStackTrace(); env.error(f.field, -1, "generic", f.field.getName() + "@" + ee.toString(), null, null); f.asm.listing(System.out); } } // Collect field constants for (Enumeration e = variables.elements() ; e.hasMoreElements() ; ) { CompilerMember f = (CompilerMember)e.nextElement(); tab.put(f.name); tab.put(f.sig); Object val = f.field.getInitialValue(); if (val != null) { tab.put((val instanceof String) ? new StringExpression(f.field.getWhere(), (String)val) : val); } } // Collect inner class constants for (Enumeration e = innerClasses.elements(); e.hasMoreElements() ; ) { ClassDefinition inner = (ClassDefinition)e.nextElement(); tab.put(inner.getClassDeclaration()); // If the inner class is local, we do not need to add its // outer class here -- the outer_class_info_index is zero. if (!inner.isLocal()) { ClassDefinition outer = inner.getOuterClass(); tab.put(outer.getClassDeclaration()); } // If the local name of the class is idNull, don't bother to // add it to the constant pool. We won't need it. Identifier inner_local_name = inner.getLocalName(); if (inner_local_name != idNull) { tab.put(inner_local_name.toString()); } } // Write header DataOutputStream data = new DataOutputStream(out); data.writeInt(JAVA_MAGIC); data.writeShort(toplevelEnv.getMinorVersion()); data.writeShort(toplevelEnv.getMajorVersion()); tab.write(env, data); // Write class information int cmods = getModifiers() & MM_CLASS; // Certain modifiers are implied: // 1. Any interface (nested or not) is implicitly deemed to be abstract, // whether it is explicitly marked so or not. (Java 1.0.) // 2. A interface which is a member of a type is implicitly deemed to // be static, whether it is explicitly marked so or not. // 3a. A type which is a member of an interface is implicitly deemed // to be public, whether it is explicitly marked so or not. // 3b. A type which is a member of an interface is implicitly deemed // to be static, whether it is explicitly marked so or not. // All of these rules are implemented in 'BatchParser.beginClass', // but the results are verified here. if (isInterface()) { // Rule 1. // The VM spec states that ACC_ABSTRACT must be set when // ACC_INTERFACE is; this was not done by javac prior to 1.2, // and the runtime compensates by setting it. Making sure // it is set here will allow the runtime hack to eventually // be removed. Rule 2 doesn't apply to transformed modifiers. assertModifiers(cmods, ACC_ABSTRACT); } else { // Contrary to the JVM spec, we only set ACC_SUPER for classes, // not interfaces. This is a workaround for a bug in IE3.0, // which refuses interfaces with ACC_SUPER on. cmods |= ACC_SUPER; } // If this is a nested class, transform access modifiers. if (outerClass != null) { // If private, transform to default (package) access. // If protected, transform to public. // M_PRIVATE and M_PROTECTED are already masked off by MM_CLASS above. // cmods &= ~(M_PRIVATE | M_PROTECTED); if (isProtected()) cmods |= M_PUBLIC; // Rule 3a. Note that Rule 3b doesn't apply to transformed modifiers. if (outerClass.isInterface()) { assertModifiers(cmods, M_PUBLIC); } } data.writeShort(cmods); if (env.dumpModifiers()) { Identifier cn = getName(); Identifier nm = Identifier.lookup(cn.getQualifier(), cn.getFlatName()); System.out.println(); System.out.println("CLASSFILE " + nm); System.out.println("---" + classModifierString(cmods)); } data.writeShort(tab.index(getClassDeclaration())); data.writeShort((getSuperClass() != null) ? tab.index(getSuperClass()) : 0); data.writeShort(interfaces.length); for (int i = 0 ; i < interfaces.length ; i++) { data.writeShort(tab.index(interfaces[i])); } // write variables ByteArrayOutputStream buf = new ByteArrayOutputStream(256); ByteArrayOutputStream attbuf = new ByteArrayOutputStream(256); DataOutputStream databuf = new DataOutputStream(buf); data.writeShort(variables.size()); for (Enumeration e = variables.elements() ; e.hasMoreElements() ; ) { CompilerMember f = (CompilerMember)e.nextElement(); Object val = f.field.getInitialValue(); data.writeShort(f.field.getModifiers() & MM_FIELD); data.writeShort(tab.index(f.name)); data.writeShort(tab.index(f.sig)); int fieldAtts = (val != null ? 1 : 0); boolean dep = f.field.isDeprecated(); boolean syn = f.field.isSynthetic(); fieldAtts += (dep ? 1 : 0) + (syn ? 1 : 0); data.writeShort(fieldAtts); if (val != null) { data.writeShort(tab.index("ConstantValue")); data.writeInt(2); data.writeShort(tab.index((val instanceof String) ? new StringExpression(f.field.getWhere(), (String)val) : val)); } if (dep) { data.writeShort(tab.index("Deprecated")); data.writeInt(0); } if (syn) { data.writeShort(tab.index("Synthetic")); data.writeInt(0); } } // write methods data.writeShort(methods.size()); for (Enumeration e = methods.elements() ; e.hasMoreElements() ; ) { CompilerMember f = (CompilerMember)e.nextElement(); int xmods = f.field.getModifiers() & MM_METHOD; // Transform floating point modifiers. M_STRICTFP // of member + status of enclosing class turn into // ACC_STRICT bit. if (((xmods & M_STRICTFP)!=0) || ((cmods & M_STRICTFP)!=0)) { xmods |= ACC_STRICT; } else { // Use the default if (env.strictdefault()) { xmods |= ACC_STRICT; } } data.writeShort(xmods); data.writeShort(tab.index(f.name)); data.writeShort(tab.index(f.sig)); ClassDeclaration exp[] = f.field.getExceptions(env); int methodAtts = ((exp.length > 0) ? 1 : 0); boolean dep = f.field.isDeprecated(); boolean syn = f.field.isSynthetic(); methodAtts += (dep ? 1 : 0) + (syn ? 1 : 0); if (!f.asm.empty()) { data.writeShort(methodAtts+1); f.asm.write(env, databuf, f.field, tab); int natts = 0; if (env.debug_lines()) { natts++; } // JCOV if (env.coverage()) { natts++; } // end JCOV if (env.debug_vars()) { natts++; } databuf.writeShort(natts); if (env.debug_lines()) { f.asm.writeLineNumberTable(env, new DataOutputStream(attbuf), tab); databuf.writeShort(tab.index("LineNumberTable")); databuf.writeInt(attbuf.size()); attbuf.writeTo(buf); attbuf.reset(); } //JCOV if (env.coverage()) { f.asm.writeCoverageTable(env, (ClassDefinition)this, new DataOutputStream(attbuf), tab, f.field.getWhere()); databuf.writeShort(tab.index("CoverageTable")); databuf.writeInt(attbuf.size()); attbuf.writeTo(buf); attbuf.reset(); } // end JCOV if (env.debug_vars()) { f.asm.writeLocalVariableTable(env, f.field, new DataOutputStream(attbuf), tab); databuf.writeShort(tab.index("LocalVariableTable")); databuf.writeInt(attbuf.size()); attbuf.writeTo(buf); attbuf.reset(); } data.writeShort(tab.index("Code")); data.writeInt(buf.size()); buf.writeTo(data); buf.reset(); } else { //JCOV if ((env.coverage()) && ((f.field.getModifiers() & M_NATIVE) > 0)) f.asm.addNativeToJcovTab(env, (ClassDefinition)this); // end JCOV data.writeShort(methodAtts); } if (exp.length > 0) { data.writeShort(tab.index("Exceptions")); data.writeInt(2 + exp.length * 2); data.writeShort(exp.length); for (int i = 0 ; i < exp.length ; i++) { data.writeShort(tab.index(exp[i])); } } if (dep) { data.writeShort(tab.index("Deprecated")); data.writeInt(0); } if (syn) { data.writeShort(tab.index("Synthetic")); data.writeInt(0); } } // class attributes data.writeShort(nClassAttrs); if (env.debug_source()) { data.writeShort(tab.index("SourceFile")); data.writeInt(2); data.writeShort(tab.index(sourceFile)); } if (this.isDeprecated()) { data.writeShort(tab.index("Deprecated")); data.writeInt(0); } if (this.isSynthetic()) { data.writeShort(tab.index("Synthetic")); data.writeInt(0); } // JCOV if (env.coverage()) { data.writeShort(tab.index("AbsoluteSourcePath")); data.writeInt(2); data.writeShort(tab.index(absoluteSourcePath)); data.writeShort(tab.index("TimeStamp")); data.writeInt(8); data.writeLong(timeStamp); } // end JCOV if (innerClasses.size() > 0) { data.writeShort(tab.index("InnerClasses")); data.writeInt(2 + 2*4*innerClasses.size()); data.writeShort(innerClasses.size()); for (Enumeration e = innerClasses.elements() ; e.hasMoreElements() ; ) { // For each inner class name transformation, we have a record // with the following fields: // // u2 inner_class_info_index; // CONSTANT_Class_info index // u2 outer_class_info_index; // CONSTANT_Class_info index // u2 inner_name_index; // CONSTANT_Utf8_info index // u2 inner_class_access_flags; // access_flags bitmask // // The spec states that outer_class_info_index is 0 iff // the inner class is not a member of its enclosing class (i.e. // it is a local or anonymous class). The spec also states // that if a class is anonymous then inner_name_index should // be 0. // // See also the initInnerClasses() method in BinaryClass.java. // Generate inner_class_info_index. ClassDefinition inner = (ClassDefinition)e.nextElement(); data.writeShort(tab.index(inner.getClassDeclaration())); // Generate outer_class_info_index. // // Checking isLocal() should probably be enough here, // but the check for isAnonymous is added for good // measure. if (inner.isLocal() || inner.isAnonymous()) { data.writeShort(0); } else { // Query: what about if inner.isInsideLocal()? // For now we continue to generate a nonzero // outer_class_info_index. ClassDefinition outer = inner.getOuterClass(); data.writeShort(tab.index(outer.getClassDeclaration())); } // Generate inner_name_index. Identifier inner_name = inner.getLocalName(); if (inner_name == idNull) { if (!inner.isAnonymous()) { throw new CompilerError("compileClass(), anonymous"); } data.writeShort(0); } else { data.writeShort(tab.index(inner_name.toString())); } // Generate inner_class_access_flags. int imods = inner.getInnerClassMember().getModifiers() & ACCM_INNERCLASS; // Certain modifiers are implied for nested types. // See rules 1, 2, 3a, and 3b enumerated above. // All of these rules are implemented in 'BatchParser.beginClass', // but are verified here. if (inner.isInterface()) { // Rules 1 and 2. assertModifiers(imods, M_ABSTRACT | M_STATIC); } if (inner.getOuterClass().isInterface()) { // Rules 3a and 3b. imods &= ~(M_PRIVATE | M_PROTECTED); // error recovery assertModifiers(imods, M_PUBLIC | M_STATIC); } data.writeShort(imods); if (env.dumpModifiers()) { Identifier fn = inner.getInnerClassMember().getName(); Identifier nm = Identifier.lookup(fn.getQualifier(), fn.getFlatName()); System.out.println("INNERCLASS " + nm); System.out.println("---" + classModifierString(imods)); } } } // Cleanup data.flush(); tab = null; // JCOV // generate coverage data if (env.covdata()) { Assembler CovAsm = new Assembler(); CovAsm.GenVecJCov(env, (ClassDefinition)this, timeStamp); } // end JCOV }
Print out the dependencies for this class (-xdepend) option
/** * Print out the dependencies for this class (-xdepend) option */
public void printClassDependencies(Environment env) { // Only do this if the -xdepend flag is on if ( toplevelEnv.print_dependencies() ) { // Name of java source file this class was in (full path) // e.g. /home/ohair/Test.java String src = ((ClassFile)getSource()).getAbsoluteName(); // Class name, fully qualified // e.g. "java.lang.Object" or "FooBar" or "sun.tools.javac.Main" // Inner class names must be mangled, as ordinary '.' qualification // is used internally where the spec requires '$' separators. // String className = getName().toString(); String className = Type.mangleInnerType(getName()).toString(); // Line number where class starts in the src file long startLine = getWhere() >> WHEREOFFSETBITS; // Line number where class ends in the src file (not used yet) long endLine = getEndPosition() >> WHEREOFFSETBITS; // First line looks like: // CLASS:src,startLine,endLine,className System.out.println( "CLASS:" + src + "," + startLine + "," + endLine + "," + className); // For each class this class is dependent on: // CLDEP:className1,className2 // where className1 is the name of the class we are in, and // classname2 is the name of the class className1 // is dependent on. for(Enumeration e = deps.elements(); e.hasMoreElements(); ) { ClassDeclaration data = (ClassDeclaration) e.nextElement(); // Mangle name of class dependend on. String depName = Type.mangleInnerType(data.getName()).toString(); env.output("CLDEP:" + className + "," + depName); } } } }