|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
public |
|
class SourceClass extends ClassDefinition { |
|
|
|
|
|
|
|
*/ |
|
Environment toplevelEnv; |
|
|
|
|
|
|
|
*/ |
|
SourceMember defConstructor; |
|
|
|
|
|
|
|
*/ |
|
ConstantPool tab = new ConstantPool(); |
|
|
|
|
|
|
|
*/ |
|
Hashtable deps = new Hashtable(11); |
|
|
|
|
|
|
|
*/ |
|
LocalMember thisArg; |
|
|
|
|
|
|
|
*/ |
|
long endPosition; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Type dummyArgumentType = null; |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
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 |
|
|
|
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); |
|
} |
|
*----*/ |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
Identifier thisName = getLocalName(); |
|
if (thisName != idNull) { |
|
// Test above suppresses error for nested anonymous classes, |
|
|
|
for (ClassDefinition scope = outerClass; scope != null; |
|
scope = scope.getOuterClass()) { |
|
Identifier outerName = scope.getLocalName(); |
|
if (thisName.equals(outerName)) |
|
env.error(where, "inner.redefined", thisName); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public long getEndPosition() { |
|
return endPosition; |
|
} |
|
|
|
public void setEndPosition(long endPosition) { |
|
this.endPosition = endPosition; |
|
} |
|
|
|
|
|
// JCOV |
|
|
|
|
|
*/ |
|
public String getAbsoluteName() { |
|
String AbsName = ((ClassFile)getSource()).getAbsoluteName(); |
|
|
|
return AbsName; |
|
} |
|
//end JCOV |
|
|
|
|
|
|
|
*/ |
|
public Imports getImports() { |
|
return toplevelEnv.getImports(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public LocalMember getThisArgument() { |
|
if (thisArg == null) { |
|
thisArg = new LocalMember(where, this, 0, getType(), idThis); |
|
} |
|
return thisArg; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
if ( toplevelEnv.print_dependencies() && c != getClassDeclaration() ) { |
|
deps.put(c,c); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addMember(Environment env, MemberDefinition f) { |
|
|
|
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); |
|
|
|
if (f.isPublic()) { |
|
f.subModifiers(M_PRIVATE | M_PROTECTED); |
|
} else { |
|
f.subModifiers(M_PRIVATE); |
|
} |
|
break; |
|
} |
|
|
|
|
|
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 { |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
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) { |
|
|
|
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(); |
|
} |
|
|
|
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); |
|
} |
|
|
|
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 |
|
|
|
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()); |
|
|
|
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); |
|
} |
|
} |
|
|
|
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 |
|
|
|
env.error(f.getWhere(), "inner.class.multidef", f); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
super.addMember(env, f); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
return new Environment(toplevelEnv, this); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean reportDeprecated(Environment env) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void noteUsedBy(ClassDefinition ref, long where, Environment env) { |
|
|
|
super.noteUsedBy(ref, where, env); |
|
ClassDefinition def = this; |
|
while (def.isInnerClass()) { |
|
def = def.getOuterClass(); |
|
} |
|
if (def.isPublic()) { |
|
return; |
|
} |
|
while (ref.isInnerClass()) { |
|
ref = ref.getOuterClass(); |
|
} |
|
if (def.getSource().equals(ref.getSource())) { |
|
return; |
|
} |
|
((SourceClass)def).checkSourceFile(env, where); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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()); |
|
|
|
((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. |
|
|
|
ClassDeclaration c = getClassDeclaration(); |
|
if (c.getStatus() == CS_PARSED) { |
|
|
|
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 |
|
|
|
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 |
|
|
|
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'. |
|
|
|
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 |
|
|
|
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? |
|
|
|
if (!isInnerClass() && !isInsideLocal()) { |
|
|
|
Identifier simpleName = nm.getName(); |
|
try { |
|
|
|
Imports imports = toplevelEnv.getImports(); |
|
Identifier ID = imports.resolve(env, simpleName); |
|
if (ID != getName()) |
|
env.error(where, "class.multidef.import", simpleName, ID); |
|
} catch (AmbiguousClass e) { |
|
|
|
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); |
|
// } |
|
|
|
|
|
if (isPublic()) { |
|
checkSourceFile(env, getWhere()); |
|
} |
|
} |
|
|
|
vset = checkMembers(env, ctx, vset); |
|
return vset; |
|
} |
|
|
|
private boolean sourceFileChecked = false; |
|
|
|
|
|
|
|
*/ |
|
public void checkSourceFile(Environment env, long where) { |
|
|
|
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 |
|
|
|
private boolean supersChecked = false; |
|
|
|
/** |
|
* 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? |
|
|
|
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'. |
|
*/ |
|
|
|
private void checkSupers(Environment env) throws ClassNotFound { |
|
|
|
|
|
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 |
|
|
|
if (getSuperClass(env) != null) { |
|
long where = getWhere(); |
|
where = IdentifierToken.getWhere(superClassId, where); |
|
try { |
|
ClassDefinition def = |
|
getSuperClass().getClassDefinition(env); |
|
|
|
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' |
|
|
|
if (!extendsCanAccess(env, getSuperClass())) { |
|
env.error(where, "cant.access.class", getSuperClass()); |
|
|
|
superClass = null; |
|
} else if (def.isFinal()) { |
|
env.error(where, "super.is.final", getSuperClass()); |
|
|
|
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, |
|
|
|
ClassDefinition sup = def; |
|
for (;;) { |
|
if (enclosingClassOf(sup)) { |
|
// Do we need a similar test for |
|
|
|
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 |
|
|
|
ClassDeclaration s = sup.getSuperClass(env); |
|
if (s == null) { |
|
|
|
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. |
|
|
|
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 |
|
|
|
if (isAnonymous()) { |
|
|
|
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 |
|
|
|
supersChecked = true; |
|
|
|
|
|
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); |
|
|
|
def.resolveTypeStructure(env); |
|
// Check superinterface access in the correct context. |
|
|
|
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); |
|
|
|
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. |
|
|
|
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 |
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Vset checkMembers(Environment env, Context ctx, Vset vset) |
|
throws ClassNotFound { |
|
|
|
|
|
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. |
|
|
|
for (MemberDefinition f = getFirstMember(); |
|
f != null; f = f.getNextMember()) { |
|
if (f.isInnerClass()) { |
|
|
|
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 |
|
|
|
if (!isInterface() && !isAbstract() && mustBeAbstract(env)) { |
|
|
|
modifiers |= M_ABSTRACT; |
|
|
|
// Tell the user which methods force this class to be abstract. |
|
|
|
|
|
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 |
|
|
|
env.error(where, "abstract.class.cannot.override", |
|
getClassDeclaration(), method, |
|
method.getDefiningClassDeclaration()); |
|
} |
|
|
|
|
|
iter = getMethods(env); |
|
while (iter.hasNext()) { |
|
// For each method, check if it is abstract. If it is, |
|
|
|
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. |
|
|
|
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 |
|
|
|
for (MemberDefinition f = getFirstMember(); |
|
f != null; f = f.getNextMember()) { |
|
if (f.isVariable() && f.isBlankFinal()) { |
|
// The following allocates a LocalMember object as a proxy |
|
|
|
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); |
|
} |
|
} |
|
} |
|
|
|
|
|
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 |
|
|
|
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. |
|
|
|
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. |
|
|
|
Vset vsCon = f.check(env, ctxInit, vsInst.copy()); |
|
|
|
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 |
|
|
|
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 |
|
|
|
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. |
|
|
|
return vset; |
|
} |
|
|
|
/** 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()); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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' |
|
|
|
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 |
|
|
|
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 |
|
|
|
if (doInheritanceChecks) { |
|
|
|
// Verify the compatibility of all inherited method definitions |
|
|
|
collectInheritedMethods(env); |
|
} |
|
|
|
basicChecking = false; |
|
basicCheckDone = true; |
|
if (tracing) env.dtExit("SourceClass.basicCheck: " + getName()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
/** |
|
* <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 |
|
|
|
ClassDefinition oc = getOuterClass(); |
|
if (oc != null && oc instanceof SourceClass |
|
&& !((SourceClass)oc).resolved) { |
|
|
|
((SourceClass)oc).resolveTypeStructure(env); |
|
// (Note: this.resolved is probably true at this point.) |
|
} |
|
|
|
// Punt if we've already resolved this class, or are currently |
|
|
|
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 |
|
|
|
resolving = true; |
|
|
|
if (tracing) |
|
env.dtEvent("SourceClass.resolveTypeStructure: RESOLVING " + getName()); |
|
|
|
env = setupEnv(env); |
|
|
|
// Resolve superclass names to class declarations |
|
|
|
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 |
|
|
|
try { |
|
checkSupers(env); |
|
} catch (ClassNotFound ee) { |
|
|
|
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 |
|
|
|
resolved = true; |
|
|
|
|
|
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); |
|
|
|
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', |
|
|
|
if (superClass == getClassDeclaration() |
|
&& getName().equals(idJavaLangObject)) { |
|
superClass = null; |
|
superClassId = null; |
|
} |
|
} |
|
|
|
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 |
|
|
|
if (tracing) env.dtExit("SourceClass.resolveSuper: " + name); |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
vset = checkInternal(env, ctx, vset); |
|
|
|
// This is now done by 'checkInternal' via its call to 'checkMembers'. |
|
// getClassDeclaration().setDefinition(this, CS_CHECKED); |
|
|
|
return vset; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void inlineLocalClass(Environment env) { |
|
for (MemberDefinition |
|
f = getFirstMember(); f != null; f = f.getNextMember()) { |
|
if ((f.isVariable() || f.isInitializer()) && !f.isStatic()) { |
|
continue; |
|
} |
|
try { |
|
((SourceMember)f).inline(env); |
|
} catch (ClassNotFound ee) { |
|
env.error(f.getWhere(), "class.not.found", ee.name, this); |
|
} |
|
} |
|
if (getReferencesFrozen() != null && !inlinedLocalClass) { |
|
inlinedLocalClass = true; |
|
|
|
for (MemberDefinition |
|
f = getFirstMember(); f != null; f = f.getNextMember()) { |
|
if (f.isConstructor()) { |
|
|
|
((SourceMember)f).addUplevelArguments(); |
|
} |
|
} |
|
} |
|
} |
|
private boolean inlinedLocalClass = false; |
|
|
|
|
|
|
|
*/ |
|
public Vset checkInsideClass(Environment env, Context ctx, Vset vset) |
|
throws ClassNotFound { |
|
if (!isInsideLocal() || isLocal()) { |
|
throw new CompilerError("checkInsideClass"); |
|
} |
|
return checkInternal(env, ctx, vset); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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()) { |
|
|
|
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"); |
|
} |
|
|
|
|
|
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; |
|
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. |
|
*/ |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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) { |
|
|
|
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 |
|
|
|
anm = idInit; |
|
|
|
SourceClass outerMostClass = (SourceClass)getTopClass(); |
|
dummyType = outerMostClass.dummyArgumentType; |
|
if (dummyType == null) { |
|
|
|
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 |
|
|
|
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, |
|
|
|
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) {}; |
|
|
|
dummyType = dummyClass.getType(); |
|
outerMostClass.dummyArgumentType = dummyType; |
|
} |
|
} else { |
|
// Otherwise, we use the name "access$N", for the |
|
|
|
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); |
|
} else { |
|
Type at[] = { t }; |
|
argTypes = at; |
|
t = Type.tMethod(Type.tVoid, argTypes); |
|
} |
|
} else { |
|
// Since constructors are never static, we don't |
|
|
|
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 |
|
|
|
Type classType = this.getType(); |
|
if (!isMethod) { |
|
if (!isUpdate) { |
|
Type at[] = { classType }; |
|
argTypes = at; |
|
t = Type.tMethod(t, argTypes); |
|
} else { |
|
Type at[] = { classType, t }; |
|
argTypes = at; |
|
t = Type.tMethod(Type.tVoid, argTypes); |
|
} |
|
} else { |
|
|
|
Type at[] = t.getArgumentTypes(); |
|
int nargs = at.length; |
|
if (field.isConstructor()) { |
|
// Access method is a constructor. |
|
|
|
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 |
|
|
|
if (at[0] != outerThisArg.getType()) { |
|
throw new CompilerError("misplaced outer this"); |
|
} |
|
// Strip outer 'this' argument. |
|
|
|
argTypes = new Type[nargs]; |
|
argTypes[0] = dummyType; |
|
for (int i = 1; i < nargs; i++) { |
|
argTypes[i] = at[i]; |
|
} |
|
} else { |
|
|
|
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. |
|
|
|
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 |
|
|
|
thisArg = new ThisExpression(where); |
|
// Remove dummy argument, as it is not |
|
|
|
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 |
|
|
|
thisArg = new IdentifierExpression(names[0]); |
|
|
|
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 { |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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) { |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SourceClass findLookupContext() { |
|
|
|
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 |
|
|
|
for (MemberDefinition f = getFirstMember(); |
|
f != null; |
|
f = f.getNextMember()) { |
|
if (f.isInnerClass()) { |
|
SourceClass lc = |
|
((SourceClass)f.getInnerClass()).findLookupContext(); |
|
if (lc != null) { |
|
return lc; |
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private MemberDefinition lookup = null; |
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getClassLiteralLookup(long fwhere) { |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
|
|
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. |
|
|
|
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, |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static Vector active = new Vector(); |
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
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; |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
if (getNestError()) { |
|
return; |
|
} |
|
|
|
int nClassAttrs = 0; |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
if (env.coverage()) { |
|
nClassAttrs += 2; |
|
tab.put("AbsoluteSourcePath"); |
|
tab.put("TimeStamp"); |
|
tab.put("CoverageTable"); |
|
} |
|
|
|
if (env.debug_vars()) { |
|
tab.put("LocalVariableTable"); |
|
} |
|
if (innerClasses.size() > 0) { |
|
tab.put("InnerClasses"); |
|
nClassAttrs += 1; |
|
} |
|
|
|
|
|
String absoluteSourcePath = ""; |
|
long timeStamp = 0; |
|
|
|
if (env.coverage()) { |
|
absoluteSourcePath = getAbsoluteName(); |
|
timeStamp = System.currentTimeMillis(); |
|
tab.put(absoluteSourcePath); |
|
} |
|
|
|
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 |
|
|
|
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); |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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 |
|
|
|
if (!inner.isLocal()) { |
|
ClassDefinition outer = inner.getOuterClass(); |
|
tab.put(outer.getClassDeclaration()); |
|
} |
|
|
|
// If the local name of the class is idNull, don't bother to |
|
|
|
Identifier inner_local_name = inner.getLocalName(); |
|
if (inner_local_name != idNull) { |
|
tab.put(inner_local_name.toString()); |
|
} |
|
} |
|
|
|
|
|
DataOutputStream data = new DataOutputStream(out); |
|
data.writeInt(JAVA_MAGIC); |
|
data.writeShort(toplevelEnv.getMinorVersion()); |
|
data.writeShort(toplevelEnv.getMajorVersion()); |
|
tab.write(env, data); |
|
|
|
|
|
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 |
|
|
|
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, |
|
|
|
cmods |= ACC_SUPER; |
|
} |
|
|
|
|
|
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. |
|
|
|
if (isProtected()) cmods |= M_PUBLIC; |
|
|
|
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])); |
|
} |
|
|
|
|
|
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 |
|
|
|
if (((xmods & M_STRICTFP)!=0) || ((cmods & M_STRICTFP)!=0)) { |
|
xmods |= ACC_STRICT; |
|
} else { |
|
|
|
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++; |
|
} |
|
|
|
if (env.coverage()) { |
|
natts++; |
|
} |
|
|
|
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(); |
|
} |
|
|
|
|
|
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(); |
|
} |
|
|
|
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 { |
|
|
|
if ((env.coverage()) && ((f.field.getModifiers() & M_NATIVE) > 0)) |
|
f.asm.addNativeToJcovTab(env, (ClassDefinition)this); |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
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. |
|
|
|
|
|
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 |
|
|
|
if (inner.isLocal() || inner.isAnonymous()) { |
|
data.writeShort(0); |
|
} else { |
|
// Query: what about if inner.isInsideLocal()? |
|
// For now we continue to generate a nonzero |
|
|
|
ClassDefinition outer = inner.getOuterClass(); |
|
data.writeShort(tab.index(outer.getClassDeclaration())); |
|
} |
|
|
|
|
|
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())); |
|
} |
|
|
|
|
|
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()) { |
|
|
|
assertModifiers(imods, M_ABSTRACT | M_STATIC); |
|
} |
|
if (inner.getOuterClass().isInterface()) { |
|
// Rules 3a and 3b. |
|
imods &= ~(M_PRIVATE | M_PROTECTED); |
|
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)); |
|
} |
|
|
|
} |
|
} |
|
|
|
|
|
data.flush(); |
|
tab = null; |
|
|
|
// JCOV |
|
|
|
if (env.covdata()) { |
|
Assembler CovAsm = new Assembler(); |
|
CovAsm.GenVecJCov(env, (ClassDefinition)this, timeStamp); |
|
} |
|
// end JCOV |
|
} |
|
|
|
/** |
|
* Print out the dependencies for this class (-xdepend) option |
|
*/ |
|
|
|
public void printClassDependencies(Environment env) { |
|
|
|
|
|
if ( toplevelEnv.print_dependencies() ) { |
|
|
|
// Name of java source file this class was in (full path) |
|
|
|
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 = Type.mangleInnerType(getName()).toString(); |
|
|
|
|
|
long startLine = getWhere() >> WHEREOFFSETBITS; |
|
|
|
|
|
long endLine = getEndPosition() >> WHEREOFFSETBITS; |
|
|
|
// First line looks like: |
|
|
|
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 |
|
|
|
for(Enumeration e = deps.elements(); e.hasMoreElements(); ) { |
|
ClassDeclaration data = (ClassDeclaration) e.nextElement(); |
|
|
|
String depName = |
|
Type.mangleInnerType(data.getName()).toString(); |
|
env.output("CLDEP:" + className + "," + depName); |
|
} |
|
} |
|
} |
|
} |