/*
 * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.java;

import java.util.Stack;
import java.io.IOException;
import sun.tools.tree.Context;
//JCOV
import java.io.File;
//end JCOV

This class defines the environment for a compilation. It is used to load classes, resolve class names and report errors. It is an abstract class, a subclass must define implementations for some of the functions.

An environment has a source object associated with it. This is the thing against which errors are reported, it is usually a file name, a field or a class.

Environments can be nested to change the source object.

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.

Author: Arthur van Hoff
/** * This class defines the environment for a compilation. * It is used to load classes, resolve class names and * report errors. It is an abstract class, a subclass * must define implementations for some of the functions.<p> * * An environment has a source object associated with it. * This is the thing against which errors are reported, it * is usually a file name, a field or a class.<p> * * Environments can be nested to change the source object.<p> * * 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. * * @author Arthur van Hoff */
public class Environment implements Constants {
The actual environment to which everything is forwarded.
/** * The actual environment to which everything is forwarded. */
Environment env;
External character encoding name
/** * External character encoding name */
String encoding;
The object that is currently being parsed/compiled. It is either a file name (String) or a field (MemberDefinition) or a class (ClassDeclaration or ClassDefinition).
/** * The object that is currently being parsed/compiled. * It is either a file name (String) or a field (MemberDefinition) * or a class (ClassDeclaration or ClassDefinition). */
Object source; public Environment(Environment env, Object source) { if (env != null && env.env != null && env.getClass() == this.getClass()) env = env.env; // a small optimization this.env = env; this.source = source; } public Environment() { this(null, null); }
Tells whether an Identifier refers to a package which should be exempt from the "exists" check in Imports#resolve().
/** * Tells whether an Identifier refers to a package which should be * exempt from the "exists" check in Imports#resolve(). */
public boolean isExemptPackage(Identifier id) { return env.isExemptPackage(id); }
Return a class declaration given a fully qualified class name.
/** * Return a class declaration given a fully qualified class name. */
public ClassDeclaration getClassDeclaration(Identifier nm) { return env.getClassDeclaration(nm); }
Return a class definition given a fully qualified class name.

Should be called only with 'internal' class names, i.e., the result of a call to 'resolveName' or a synthetic class name.

/** * Return a class definition given a fully qualified class name. * <p> * Should be called only with 'internal' class names, i.e., the result * of a call to 'resolveName' or a synthetic class name. */
public final ClassDefinition getClassDefinition(Identifier nm) throws ClassNotFound { if (nm.isInner()) { ClassDefinition c = getClassDefinition(nm.getTopName()); Identifier tail = nm.getFlatName(); walkTail: while (tail.isQualified()) { tail = tail.getTail(); Identifier head = tail.getHead(); //System.out.println("CLASS: " + c + " HEAD: " + head + " TAIL: " + tail); String hname = head.toString(); // If the name is of the form 'ClassName.N$localName', where N is // a number, the field 'N$localName' may not necessarily be a member // of the class named by 'ClassName', but might be a member of some // inaccessible class contained within it. We use 'getLocalClass' // to do the lookup in this case. This is part of a fix for bugid // 4054523 and 4030421. See also 'BatchEnvironment.makeClassDefinition'. // This should also work for anonymous class names of the form // 'ClassName.N'. Note that the '.' qualifications get converted to // '$' characters when determining the external name of the class and // the name of the class file. if (hname.length() > 0 && Character.isDigit(hname.charAt(0))) { ClassDefinition localClass = c.getLocalClass(hname); if (localClass != null) { c = localClass; continue walkTail; } } else { for (MemberDefinition f = c.getFirstMatch(head); f != null; f = f.getNextMatch()) { if (f.isInnerClass()) { c = f.getInnerClass(); continue walkTail; } } } throw new ClassNotFound(Identifier.lookupInner(c.getName(), head)); } //System.out.println("FOUND " + c + " FOR " + nm); return c; } return getClassDeclaration(nm).getClassDefinition(this); }
Return a class declaration given a type. Only works for class types.
/** * Return a class declaration given a type. Only works for * class types. */
public ClassDeclaration getClassDeclaration(Type t) { return getClassDeclaration(t.getClassName()); }
Return a class definition given a type. Only works for class types.
/** * Return a class definition given a type. Only works for * class types. */
public final ClassDefinition getClassDefinition(Type t) throws ClassNotFound { return getClassDefinition(t.getClassName()); }
Check if a class exists (without actually loading it). (Since inner classes cannot in general be examined without loading source, this method does not accept inner names.)
/** * Check if a class exists (without actually loading it). * (Since inner classes cannot in general be examined without * loading source, this method does not accept inner names.) */
public boolean classExists(Identifier nm) { return env.classExists(nm); } public final boolean classExists(Type t) { return !t.isType(TC_CLASS) || classExists(t.getClassName()); }
Get the package path for a package
/** * Get the package path for a package */
public Package getPackage(Identifier pkg) throws IOException { return env.getPackage(pkg); }
Load the definition of a class.
/** * Load the definition of a class. */
public void loadDefinition(ClassDeclaration c) { env.loadDefinition(c); }
Return the source of the environment (ie: the thing being compiled/parsed).
/** * Return the source of the environment (ie: the thing being compiled/parsed). */
public final Object getSource() { return source; }
Resolve a type. Make sure that all the classes referred to by the type have a definition. Report errors. Return true if the type is well-formed. Presently used for types appearing in member declarations, which represent named types internally as qualified identifiers. Type names appearing in local variable declarations and within expressions are represented as identifier or field expressions, and are resolved by 'toType', which delegates handling of the non-inner portion of the name to this method.

In 'toType', the various stages of qualification are represented by separate AST nodes. Here, we are given a single identifier which contains the entire qualification structure. It is not possible in general to set the error location to the exact position of a component that is in error, so an error message must refer to the entire qualified name. An attempt to keep track of the string length of the components of the name and to offset the location accordingly fails because the initial prefix of the name may have been rewritten by an earlier call to 'resolveName'. See 'SourceMember.resolveTypeStructure'. The situation is actually even worse than this, because only a single location is passed in for an entire declaration, which may contain many type names. All error messages are thus poorly localized. These checks should be done while traversing the parse tree for the type, not the type descriptor.

DESIGN NOTE: As far as I can tell, the two-stage resolution of names represented in string form is an artifact of the late implementation of inner classes and the use of mangled names internally within the compiler. All qualified names should have their hiearchical structure made explicit in the parse tree at the phase at which they are presented for static semantic checking. This would affect class names appearing in 'extends', 'implements', and 'throws' clauses, as well as in member declarations.

/** * Resolve a type. Make sure that all the classes referred to by * the type have a definition. Report errors. Return true if * the type is well-formed. Presently used for types appearing * in member declarations, which represent named types internally as * qualified identifiers. Type names appearing in local variable * declarations and within expressions are represented as identifier * or field expressions, and are resolved by 'toType', which delegates * handling of the non-inner portion of the name to this method. * <p> * In 'toType', the various stages of qualification are represented by * separate AST nodes. Here, we are given a single identifier which * contains the entire qualification structure. It is not possible in * general to set the error location to the exact position of a component * that is in error, so an error message must refer to the entire qualified * name. An attempt to keep track of the string length of the components of * the name and to offset the location accordingly fails because the initial * prefix of the name may have been rewritten by an earlier call to * 'resolveName'. See 'SourceMember.resolveTypeStructure'. The situation * is actually even worse than this, because only a single location is * passed in for an entire declaration, which may contain many type names. * All error messages are thus poorly localized. These checks should be * done while traversing the parse tree for the type, not the type descriptor. * <p> * DESIGN NOTE: * As far as I can tell, the two-stage resolution of names represented in * string form is an artifact of the late implementation of inner classes * and the use of mangled names internally within the compiler. All * qualified names should have their hiearchical structure made explicit * in the parse tree at the phase at which they are presented for static * semantic checking. This would affect class names appearing in 'extends', * 'implements', and 'throws' clauses, as well as in member declarations. */
public boolean resolve(long where, ClassDefinition c, Type t) { switch (t.getTypeCode()) { case TC_CLASS: { ClassDefinition def; try { Identifier nm = t.getClassName(); if (!nm.isQualified() && !nm.isInner() && !classExists(nm)) { resolve(nm); // elicit complaints about ambiguity } def = getQualifiedClassDefinition(where, nm, c, false); if (!c.canAccess(this, def.getClassDeclaration())) { // Reported error location may be imprecise // if the name is qualified. error(where, "cant.access.class", def); return true; // return false later } def.noteUsedBy(c, where, env); } catch (AmbiguousClass ee) { error(where, "ambig.class", ee.name1, ee.name2); return false; } catch (ClassNotFound e) { // For now, report "class.and.package" only when the code // is going to fail anyway. try { if (e.name.isInner() && getPackage(e.name.getTopName()).exists()) { env.error(where, "class.and.package", e.name.getTopName()); } } catch (IOException ee) { env.error(where, "io.exception", "package check"); } // This error message is also emitted for 'new' expressions. // error(where, "class.not.found", e.name, "declaration"); error(where, "class.not.found.no.context", e.name); return false; } return true; } case TC_ARRAY: return resolve(where, c, t.getElementType()); case TC_METHOD: boolean ok = resolve(where, c, t.getReturnType()); Type args[] = t.getArgumentTypes(); for (int i = args.length ; i-- > 0 ; ) { ok &= resolve(where, c, args[i]); } return ok; } return true; }
Given its fully-qualified name, verify that a class is defined and accessible. Used to check components of qualified names in contexts where a class is expected. Like 'resolve', but is given a single type name, not a type descriptor.
/** * Given its fully-qualified name, verify that a class is defined and accessible. * Used to check components of qualified names in contexts where a class is expected. * Like 'resolve', but is given a single type name, not a type descriptor. */
public boolean resolveByName(long where, ClassDefinition c, Identifier nm) { return resolveByName(where, c, nm, false); } public boolean resolveExtendsByName(long where, ClassDefinition c, Identifier nm) { return resolveByName(where, c, nm, true); } private boolean resolveByName(long where, ClassDefinition c, Identifier nm, boolean isExtends) { ClassDefinition def; try { if (!nm.isQualified() && !nm.isInner() && !classExists(nm)) { resolve(nm); // elicit complaints about ambiguity } def = getQualifiedClassDefinition(where, nm, c, isExtends); ClassDeclaration decl = def.getClassDeclaration(); if (!((!isExtends && c.canAccess(this, decl)) || (isExtends && c.extendsCanAccess(this, decl)))) { error(where, "cant.access.class", def); return true; // return false later } } catch (AmbiguousClass ee) { error(where, "ambig.class", ee.name1, ee.name2); return false; } catch (ClassNotFound e) { // For now, report "class.and.package" only when the code // is going to fail anyway. try { if (e.name.isInner() && getPackage(e.name.getTopName()).exists()) { env.error(where, "class.and.package", e.name.getTopName()); } } catch (IOException ee) { env.error(where, "io.exception", "package check"); } error(where, "class.not.found", e.name, "type name"); return false; } return true; }
Like 'getClassDefinition(env)', but check access on each component. Currently called only by 'resolve' above. It is doubtful that calls to 'getClassDefinition(env)' are appropriate now.
/** * Like 'getClassDefinition(env)', but check access on each component. * Currently called only by 'resolve' above. It is doubtful that calls * to 'getClassDefinition(env)' are appropriate now. */
public final ClassDefinition getQualifiedClassDefinition(long where, Identifier nm, ClassDefinition ctxClass, boolean isExtends) throws ClassNotFound { if (nm.isInner()) { ClassDefinition c = getClassDefinition(nm.getTopName()); Identifier tail = nm.getFlatName(); walkTail: while (tail.isQualified()) { tail = tail.getTail(); Identifier head = tail.getHead(); // System.out.println("CLASS: " + c + " HEAD: " + head + " TAIL: " + tail); String hname = head.toString(); // Handle synthesized names of local and anonymous classes. // See 'getClassDefinition(env)' above. if (hname.length() > 0 && Character.isDigit(hname.charAt(0))) { ClassDefinition localClass = c.getLocalClass(hname); if (localClass != null) { c = localClass; continue walkTail; } } else { for (MemberDefinition f = c.getFirstMatch(head); f != null; f = f.getNextMatch()) { if (f.isInnerClass()) { ClassDeclaration rdecl = c.getClassDeclaration(); c = f.getInnerClass(); ClassDeclaration fdecl = c.getClassDeclaration(); // This check is presumably applicable even if the // original source-code name (expanded by 'resolveNames') // was a simple, unqualified name. Hopefully, JLS 2e // will clarify the matter. if ((!isExtends && !ctxClass.canAccess(env, fdecl)) || (isExtends && !ctxClass.extendsCanAccess(env, fdecl))) { // Reported error location is imprecise. env.error(where, "no.type.access", head, rdecl, ctxClass); } // The JLS 6.6.2 restrictions on access to protected members // depend in an essential way upon the syntactic form of the name. // Since the compiler has previously expanded the class names // here into fully-qualified form ('resolveNames'), this check // cannot be performed here. Unfortunately, the original names // are clobbered during 'basicCheck', which is also the phase that // resolves the inheritance structure, required to implement the // access restrictions. Pending a large-scale revision of the // name-resolution machinery, we forgo this check, with the result // that the JLS 6.6.2 restrictions are not enforced for some cases // of qualified access to inner classes. Some qualified names are // resolved elsewhere via a different mechanism, and will be // treated correctly -- see 'FieldExpression.checkCommon'. /*---------------------------------------* if (f.isProtected()) { Type rty = Type.tClass(rdecl.getName()); // hack if (!ctxClass.protectedAccess(env, f, rty)) { // Reported error location is imprecise. env.error(where, "invalid.protected.type.use", head, ctxClass, rty); } } *---------------------------------------*/ continue walkTail; } } } throw new ClassNotFound(Identifier.lookupInner(c.getName(), head)); } //System.out.println("FOUND " + c + " FOR " + nm); return c; } return getClassDeclaration(nm).getClassDefinition(this); }
Resolve the names within a type, returning the adjusted type. Adjust class names to reflect scoping. Do not report errors.

NOTE: It would be convenient to check for errors here, such as verifying that each component of a qualified name exists and is accessible. Why must this be done in a separate phase?

If the 'synth' argument is true, indicating that the member whose type is being resolved is synthetic, names are resolved with respect to the package scope. (Fix for 4097882)

/** * Resolve the names within a type, returning the adjusted type. * Adjust class names to reflect scoping. * Do not report errors. * <p> * NOTE: It would be convenient to check for errors here, such as * verifying that each component of a qualified name exists and is * accessible. Why must this be done in a separate phase? * <p> * If the 'synth' argument is true, indicating that the member whose * type is being resolved is synthetic, names are resolved with respect * to the package scope. (Fix for 4097882) */
public Type resolveNames(ClassDefinition c, Type t, boolean synth) { if (tracing) dtEvent("Environment.resolveNames: " + c + ", " + t); switch (t.getTypeCode()) { case TC_CLASS: { Identifier name = t.getClassName(); Identifier rname; if (synth) { rname = resolvePackageQualifiedName(name); } else { rname = c.resolveName(this, name); } if (name != rname) { t = Type.tClass(rname); } break; } case TC_ARRAY: t = Type.tArray(resolveNames(c, t.getElementType(), synth)); break; case TC_METHOD: { Type ret = t.getReturnType(); Type rret = resolveNames(c, ret, synth); Type args[] = t.getArgumentTypes(); Type rargs[] = new Type[args.length]; boolean changed = (ret != rret); for (int i = args.length ; i-- > 0 ; ) { Type arg = args[i]; Type rarg = resolveNames(c, arg, synth); rargs[i] = rarg; if (arg != rarg) { changed = true; } } if (changed) { t = Type.tMethod(rret, rargs); } break; } } return t; }
Resolve a class name, using only package and import directives. Report no errors.

/** * Resolve a class name, using only package and import directives. * Report no errors. * <p> */
public Identifier resolveName(Identifier name) { // This logic is pretty exactly parallel to that of // ClassDefinition.resolveName(). if (name.isQualified()) { // Try to resolve the first identifier component, // because inner class names take precedence over // package prefixes. (Cf. ClassDefinition.resolveName.) Identifier rhead = resolveName(name.getHead()); if (rhead.hasAmbigPrefix()) { // The first identifier component refers to an // ambiguous class. Limp on. We throw away the // rest of the classname as it is irrelevant. // (part of solution for 4059855). return rhead; } if (!this.classExists(rhead)) { return this.resolvePackageQualifiedName(name); } try { return this.getClassDefinition(rhead). resolveInnerClass(this, name.getTail()); } catch (ClassNotFound ee) { // return partially-resolved name someone else can fail on return Identifier.lookupInner(rhead, name.getTail()); } } try { return resolve(name); } catch (AmbiguousClass ee) { // Don't force a resolution of the name if it is ambiguous. // Forcing the resolution would tack the current package // name onto the front of the class, which would be wrong. // Instead, mark the name as ambiguous and let a later stage // find the error by calling env.resolve(name). // (part of solution for 4059855). if (name.hasAmbigPrefix()) { return name; } else { return name.addAmbigPrefix(); } } catch (ClassNotFound ee) { // last chance to make something halfway sensible Imports imports = getImports(); if (imports != null) return imports.forceResolve(this, name); } return name; }
Discover if name consists of a package prefix, followed by the name of a class (that actually exists), followed possibly by some inner class names. If we can't find a class that exists, return the name unchanged.

This routine is used after a class name fails to be resolved by means of imports or inner classes. However, import processing uses this routine directly, since import names must be exactly qualified to start with.

/** * Discover if name consists of a package prefix, followed by the * name of a class (that actually exists), followed possibly by * some inner class names. If we can't find a class that exists, * return the name unchanged. * <p> * This routine is used after a class name fails to * be resolved by means of imports or inner classes. * However, import processing uses this routine directly, * since import names must be exactly qualified to start with. */
public final Identifier resolvePackageQualifiedName(Identifier name) { Identifier tail = null; for (;;) { if (classExists(name)) { break; } if (!name.isQualified()) { name = (tail == null) ? name : Identifier.lookup(name, tail); tail = null; break; } Identifier nm = name.getName(); tail = (tail == null)? nm: Identifier.lookup(nm, tail); name = name.getQualifier(); } if (tail != null) name = Identifier.lookupInner(name, tail); return name; }
Resolve a class name, using only package and import directives.
/** * Resolve a class name, using only package and import directives. */
public Identifier resolve(Identifier nm) throws ClassNotFound { if (env == null) return nm; // a pretty useless no-op return env.resolve(nm); }
Get the imports used to resolve class names.
/** * Get the imports used to resolve class names. */
public Imports getImports() { if (env == null) return null; // lame default return env.getImports(); }
Create a new class.
/** * Create a new class. */
public ClassDefinition makeClassDefinition(Environment origEnv, long where, IdentifierToken name, String doc, int modifiers, IdentifierToken superClass, IdentifierToken interfaces[], ClassDefinition outerClass) { if (env == null) return null; // lame default return env.makeClassDefinition(origEnv, where, name, doc, modifiers, superClass, interfaces, outerClass); }
Create a new field.
/** * Create a new field. */
public MemberDefinition makeMemberDefinition(Environment origEnv, long where, ClassDefinition clazz, String doc, int modifiers, Type type, Identifier name, IdentifierToken argNames[], IdentifierToken expIds[], Object value) { if (env == null) return null; // lame default return env.makeMemberDefinition(origEnv, where, clazz, doc, modifiers, type, name, argNames, expIds, value); }
Returns true if the given method is applicable to the given arguments
/** * Returns true if the given method is applicable to the given arguments */
public boolean isApplicable(MemberDefinition m, Type args[]) throws ClassNotFound { Type mType = m.getType(); if (!mType.isType(TC_METHOD)) return false; Type mArgs[] = mType.getArgumentTypes(); if (args.length != mArgs.length) return false; for (int i = args.length ; --i >= 0 ;) if (!isMoreSpecific(args[i], mArgs[i])) return false; return true; }
Returns true if "best" is in every argument at least as good as "other"
/** * Returns true if "best" is in every argument at least as good as "other" */
public boolean isMoreSpecific(MemberDefinition best, MemberDefinition other) throws ClassNotFound { Type bestType = best.getClassDeclaration().getType(); Type otherType = other.getClassDeclaration().getType(); boolean result = isMoreSpecific(bestType, otherType) && isApplicable(other, best.getType().getArgumentTypes()); // System.out.println("isMoreSpecific: " + best + "/" + other // + " => " + result); return result; }
Returns true if "from" is a more specific type than "to"
/** * Returns true if "from" is a more specific type than "to" */
public boolean isMoreSpecific(Type from, Type to) throws ClassNotFound { return implicitCast(from, to); }
Return true if an implicit cast from this type to the given type is allowed.
/** * Return true if an implicit cast from this type to * the given type is allowed. */
public boolean implicitCast(Type from, Type to) throws ClassNotFound { if (from == to) return true; int toTypeCode = to.getTypeCode(); switch(from.getTypeCode()) { case TC_BYTE: if (toTypeCode == TC_SHORT) return true; case TC_SHORT: case TC_CHAR: if (toTypeCode == TC_INT) return true; case TC_INT: if (toTypeCode == TC_LONG) return true; case TC_LONG: if (toTypeCode == TC_FLOAT) return true; case TC_FLOAT: if (toTypeCode == TC_DOUBLE) return true; case TC_DOUBLE: default: return false; case TC_NULL: return to.inMask(TM_REFERENCE); case TC_ARRAY: if (!to.isType(TC_ARRAY)) { return (to == Type.tObject || to == Type.tCloneable || to == Type.tSerializable); } else { // both are arrays. recurse down both until one isn't an array do { from = from.getElementType(); to = to.getElementType(); } while (from.isType(TC_ARRAY) && to.isType(TC_ARRAY)); if ( from.inMask(TM_ARRAY|TM_CLASS) && to.inMask(TM_ARRAY|TM_CLASS)) { return isMoreSpecific(from, to); } else { return (from.getTypeCode() == to.getTypeCode()); } } case TC_CLASS: if (toTypeCode == TC_CLASS) { ClassDefinition fromDef = getClassDefinition(from); ClassDefinition toDef = getClassDefinition(to); return toDef.implementedBy(this, fromDef.getClassDeclaration()); } else { return false; } } }
Return true if an explicit cast from this type to the given type is allowed.
/** * Return true if an explicit cast from this type to * the given type is allowed. */
public boolean explicitCast(Type from, Type to) throws ClassNotFound { if (implicitCast(from, to)) { return true; } if (from.inMask(TM_NUMBER)) { return to.inMask(TM_NUMBER); } if (from.isType(TC_CLASS) && to.isType(TC_CLASS)) { ClassDefinition fromClass = getClassDefinition(from); ClassDefinition toClass = getClassDefinition(to); if (toClass.isFinal()) { return fromClass.implementedBy(this, toClass.getClassDeclaration()); } if (fromClass.isFinal()) { return toClass.implementedBy(this, fromClass.getClassDeclaration()); } // The code here used to omit this case. If both types // involved in a cast are interfaces, then JLS 5.5 requires // that we do a simple test -- make sure none of the methods // in toClass and fromClass have the same signature but // different return types. (bug number 4028359) if (toClass.isInterface() && fromClass.isInterface()) { return toClass.couldImplement(fromClass); } return toClass.isInterface() || fromClass.isInterface() || fromClass.superClassOf(this, toClass.getClassDeclaration()); } if (to.isType(TC_ARRAY)) { if (from.isType(TC_ARRAY)) { Type t1 = from.getElementType(); Type t2 = to.getElementType(); while ((t1.getTypeCode() == TC_ARRAY) && (t2.getTypeCode() == TC_ARRAY)) { t1 = t1.getElementType(); t2 = t2.getElementType(); } if (t1.inMask(TM_ARRAY|TM_CLASS) && t2.inMask(TM_ARRAY|TM_CLASS)) { return explicitCast(t1, t2); } } else if (from == Type.tObject || from == Type.tCloneable || from == Type.tSerializable) return true; } return false; }
Flags.
/** * Flags. */
public int getFlags() { return env.getFlags(); }
Debugging flags. There used to be a method debug() that has been replaced because -g has changed meaning (it now cooperates with -O and line number, variable range and source file info can be toggled separately).
/** * Debugging flags. There used to be a method debug() * that has been replaced because -g has changed meaning * (it now cooperates with -O and line number, variable * range and source file info can be toggled separately). */
public final boolean debug_lines() { return (getFlags() & F_DEBUG_LINES) != 0; } public final boolean debug_vars() { return (getFlags() & F_DEBUG_VARS) != 0; } public final boolean debug_source() { return (getFlags() & F_DEBUG_SOURCE) != 0; }
Optimization flags. There used to be a method optimize() that has been replaced because -O has changed meaning in javac to be replaced with -O and -O:interclass.
/** * Optimization flags. There used to be a method optimize() * that has been replaced because -O has changed meaning in * javac to be replaced with -O and -O:interclass. */
public final boolean opt() { return (getFlags() & F_OPT) != 0; } public final boolean opt_interclass() { return (getFlags() & F_OPT_INTERCLASS) != 0; }
Verbose
/** * Verbose */
public final boolean verbose() { return (getFlags() & F_VERBOSE) != 0; }
Dump debugging stuff
/** * Dump debugging stuff */
public final boolean dump() { return (getFlags() & F_DUMP) != 0; }
Verbose
/** * Verbose */
public final boolean warnings() { return (getFlags() & F_WARNINGS) != 0; }
Dependencies
/** * Dependencies */
public final boolean dependencies() { return (getFlags() & F_DEPENDENCIES) != 0; }
Print Dependencies to stdout
/** * Print Dependencies to stdout */
public final boolean print_dependencies() { return (getFlags() & F_PRINT_DEPENDENCIES) != 0; }
Deprecation warnings are enabled.
/** * Deprecation warnings are enabled. */
public final boolean deprecation() { return (getFlags() & F_DEPRECATION) != 0; }
Do not support virtual machines before version 1.2. This option is not supported and is only here for testing purposes.
/** * Do not support virtual machines before version 1.2. * This option is not supported and is only here for testing purposes. */
public final boolean version12() { return (getFlags() & F_VERSION12) != 0; }
Floating point is strict by default
/** * Floating point is strict by default */
public final boolean strictdefault() { return (getFlags() & F_STRICTDEFAULT) != 0; }
Release resources, if any.
/** * Release resources, if any. */
public void shutdown() { if (env != null) { env.shutdown(); } }
Issue an error. source - the input source, usually a file name string offset - the offset in the source of the error err - the error number (as defined in this interface) arg1 - an optional argument to the error (null if not applicable) arg2 - a second optional argument to the error (null if not applicable) arg3 - a third optional argument to the error (null if not applicable)
/** * Issue an error. * source - the input source, usually a file name string * offset - the offset in the source of the error * err - the error number (as defined in this interface) * arg1 - an optional argument to the error (null if not applicable) * arg2 - a second optional argument to the error (null if not applicable) * arg3 - a third optional argument to the error (null if not applicable) */
public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) { env.error(source, where, err, arg1, arg2, arg3); } public final void error(long where, String err, Object arg1, Object arg2, Object arg3) { error(source, where, err, arg1, arg2, arg3); } public final void error(long where, String err, Object arg1, Object arg2) { error(source, where, err, arg1, arg2, null); } public final void error(long where, String err, Object arg1) { error(source, where, err, arg1, null, null); } public final void error(long where, String err) { error(source, where, err, null, null, null); }
Output a string. This can either be an error message or something for debugging. This should be used instead of println.
/** * Output a string. This can either be an error message or something * for debugging. This should be used instead of println. */
public void output(String msg) { env.output(msg); } private static boolean debugging = (System.getProperty("javac.debug") != null); public static void debugOutput(Object msg) { if (Environment.debugging) System.out.println(msg.toString()); }
set character encoding name
/** * set character encoding name */
public void setCharacterEncoding(String encoding) { this.encoding = encoding; }
Return character encoding name
/** * Return character encoding name */
public String getCharacterEncoding() { return encoding; }
Return major version to use in generated class files.
/** * Return major version to use in generated class files. */
public short getMajorVersion() { if (env==null) return JAVA_DEFAULT_VERSION; // needed for javah return env.getMajorVersion(); }
Return minor version to use in generated class files.
/** * Return minor version to use in generated class files. */
public short getMinorVersion() { if (env==null) return JAVA_DEFAULT_MINOR_VERSION; // needed for javah return env.getMinorVersion(); } // JCOV
get coverage flag
/** * get coverage flag */
public final boolean coverage() { return (getFlags() & F_COVERAGE) != 0; }
get flag of generation the coverage data file
/** * get flag of generation the coverage data file */
public final boolean covdata() { return (getFlags() & F_COVDATA) != 0; }
Return the coverage data file
/** * Return the coverage data file */
public File getcovFile() { return env.getcovFile(); } // end JCOV
Debug tracing. Currently, this code is used only for tracing the loading and checking of classes, particularly the demand-driven aspects. This code should probably be integrated with 'debugOutput' above, but we need to give more thought to the issue of classifying debugging messages and allowing those only those of interest to be enabled. Calls to these methods are generally conditioned on the final variable 'Constants.tracing', which allows the calls to be completely omitted in a production release to avoid space and time overhead.
/** * Debug tracing. * Currently, this code is used only for tracing the loading and * checking of classes, particularly the demand-driven aspects. * This code should probably be integrated with 'debugOutput' above, * but we need to give more thought to the issue of classifying debugging * messages and allowing those only those of interest to be enabled. * * Calls to these methods are generally conditioned on the final variable * 'Constants.tracing', which allows the calls to be completely omitted * in a production release to avoid space and time overhead. */
private static boolean dependtrace = (System.getProperty("javac.trace.depend") != null); public void dtEnter(String s) { if (dependtrace) System.out.println(">>> " + s); } public void dtExit(String s) { if (dependtrace) System.out.println("<<< " + s); } public void dtEvent(String s) { if (dependtrace) System.out.println(s); }
Enable diagnostic dump of class modifier bits, including those in InnerClasses attributes, as they are written to the classfile. In the future, may also enable dumping field and method modifiers.
/** * Enable diagnostic dump of class modifier bits, including those * in InnerClasses attributes, as they are written to the classfile. * In the future, may also enable dumping field and method modifiers. */
private static boolean dumpmodifiers = (System.getProperty("javac.dump.modifiers") != null); public boolean dumpModifiers() { return dumpmodifiers; } }