|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.java; |
|
|
|
import java.io.IOException; |
|
import java.io.DataInputStream; |
|
import java.io.OutputStream; |
|
import java.io.DataOutputStream; |
|
import java.io.ByteArrayInputStream; |
|
import java.util.Hashtable; |
|
import java.util.Vector; |
|
import java.util.Enumeration; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final |
|
class BinaryClass extends ClassDefinition implements Constants { |
|
BinaryConstantPool cpool; |
|
BinaryAttribute atts; |
|
Vector dependencies; |
|
private boolean haveLoadedNested = false; |
|
|
|
|
|
|
|
*/ |
|
public BinaryClass(Object source, ClassDeclaration declaration, int modifiers, |
|
ClassDeclaration superClass, ClassDeclaration interfaces[], |
|
Vector dependencies) { |
|
super(source, 0, declaration, modifiers, null, null); |
|
this.dependencies = dependencies; |
|
this.superClass = superClass; |
|
this.interfaces = interfaces; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean basicCheckDone = false; |
|
private boolean basicChecking = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void basicCheck(Environment env) throws ClassNotFound { |
|
if (tracing) env.dtEnter("BinaryClass.basicCheck: " + getName()); |
|
|
|
// We need to guard against duplicate calls to basicCheck(). They |
|
// can lead to calling collectInheritedMethods() for this class |
|
// from within a previous call to collectInheritedMethods() for |
|
// this class. That is not allowed. |
|
|
|
if (basicChecking || basicCheckDone) { |
|
if (tracing) env.dtExit("BinaryClass.basicCheck: OK " + getName()); |
|
return; |
|
} |
|
|
|
if (tracing) env.dtEvent("BinaryClass.basicCheck: CHECKING " + getName()); |
|
basicChecking = true; |
|
|
|
super.basicCheck(env); |
|
|
|
|
|
if (doInheritanceChecks) { |
|
collectInheritedMethods(env); |
|
} |
|
|
|
basicCheckDone = true; |
|
basicChecking = false; |
|
if (tracing) env.dtExit("BinaryClass.basicCheck: " + getName()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static BinaryClass load(Environment env, DataInputStream in) throws IOException { |
|
return load(env, in, ~(ATT_CODE|ATT_ALLCLASSES)); |
|
} |
|
|
|
public static BinaryClass load(Environment env, |
|
DataInputStream in, int mask) throws IOException { |
|
// Read the header |
|
int magic = in.readInt(); |
|
if (magic != JAVA_MAGIC) { |
|
throw new ClassFormatError("wrong magic: " + magic + ", expected " + JAVA_MAGIC); |
|
} |
|
int minor_version = in.readUnsignedShort(); |
|
int version = in.readUnsignedShort(); |
|
if (version < JAVA_MIN_SUPPORTED_VERSION) { |
|
throw new ClassFormatError( |
|
sun.tools.javac.Main.getText( |
|
"javac.err.version.too.old", |
|
String.valueOf(version))); |
|
} else if ((version > JAVA_MAX_SUPPORTED_VERSION) |
|
|| (version == JAVA_MAX_SUPPORTED_VERSION |
|
&& minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) { |
|
throw new ClassFormatError( |
|
sun.tools.javac.Main.getText( |
|
"javac.err.version.too.recent", |
|
version+"."+minor_version)); |
|
} |
|
|
|
|
|
BinaryConstantPool cpool = new BinaryConstantPool(in); |
|
|
|
|
|
Vector dependencies = cpool.getDependencies(env); |
|
|
|
// Read modifiers |
|
int classMod = in.readUnsignedShort() & ACCM_CLASS; |
|
|
|
|
|
ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort()); |
|
|
|
|
|
ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort()); |
|
|
|
|
|
ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()]; |
|
for (int i = 0 ; i < interfaces.length ; i++) { |
|
|
|
interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort()); |
|
} |
|
|
|
|
|
BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl, |
|
interfaces, dependencies); |
|
c.cpool = cpool; |
|
|
|
|
|
c.addDependency(superClassDecl); |
|
|
|
// Read the fields |
|
int nfields = in.readUnsignedShort(); |
|
for (int i = 0 ; i < nfields ; i++) { |
|
|
|
int fieldMod = in.readUnsignedShort() & ACCM_FIELD; |
|
|
|
Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort()); |
|
|
|
Type fieldType = cpool.getType(in.readUnsignedShort()); |
|
BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask); |
|
c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts)); |
|
} |
|
|
|
// Read the methods |
|
int nmethods = in.readUnsignedShort(); |
|
for (int i = 0 ; i < nmethods ; i++) { |
|
|
|
int methMod = in.readUnsignedShort() & ACCM_METHOD; |
|
|
|
Identifier methName = cpool.getIdentifier(in.readUnsignedShort()); |
|
|
|
Type methType = cpool.getType(in.readUnsignedShort()); |
|
BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask); |
|
c.addMember(new BinaryMember(c, methMod, methType, methName, atts)); |
|
} |
|
|
|
|
|
c.atts = BinaryAttribute.load(in, cpool, mask); |
|
|
|
|
|
byte data[] = c.getAttribute(idSourceFile); |
|
if (data != null) { |
|
DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data)); |
|
|
|
c.source = cpool.getString(dataStream.readUnsignedShort()); |
|
} |
|
|
|
|
|
data = c.getAttribute(idDocumentation); |
|
if (data != null) { |
|
c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF(); |
|
} |
|
|
|
|
|
if (c.getAttribute(idDeprecated) != null) { |
|
c.modifiers |= M_DEPRECATED; |
|
} |
|
|
|
|
|
if (c.getAttribute(idSynthetic) != null) { |
|
c.modifiers |= M_SYNTHETIC; |
|
} |
|
|
|
return c; |
|
} |
|
|
|
/** |
|
* Called when an environment ties a binary definition to a declaration. |
|
* At this point, auxiliary definitions may be loaded. |
|
*/ |
|
|
|
public void loadNested(Environment env) { |
|
loadNested(env, 0); |
|
} |
|
|
|
public void loadNested(Environment env, int flags) { |
|
|
|
if (haveLoadedNested) { |
|
// Duplicate calls most likely should not occur, but they do |
|
// in javap. Be tolerant of them for the time being. |
|
|
|
if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED"); |
|
return; |
|
} |
|
haveLoadedNested = true; |
|
|
|
try { |
|
byte data[]; |
|
data = getAttribute(idInnerClasses); |
|
if (data != null) { |
|
initInnerClasses(env, data, flags); |
|
} |
|
} catch (IOException ee) { |
|
// The inner classes attribute is not well-formed. |
|
// It may, for example, contain no data. Report this. |
|
|
|
env.error(0, "malformed.attribute", getClassDeclaration(), |
|
idInnerClasses); |
|
if (tracing) |
|
env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)"); |
|
} |
|
} |
|
|
|
private void initInnerClasses(Environment env, |
|
byte data[], |
|
int flags) throws IOException { |
|
DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data)); |
|
int nrec = ds.readUnsignedShort(); |
|
for (int i = 0; i < nrec; i++) { |
|
// 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. |
|
// |
|
// Prior to jdk1.2, javac did not implement the spec. Instead |
|
// it <em>always</em> set outer_class_info_index to the |
|
// enclosing outer class and if the class was anonymous, |
|
// it set inner_name_index to be the index of a CONSTANT_Utf8 |
|
// entry containing the null string "" (idNull). This code is |
|
// designed to handle either kind of class file. |
|
// |
|
// See also the compileClass() method in SourceClass.java. |
|
|
|
// Read in the inner_class_info |
|
|
|
int inner_index = ds.readUnsignedShort(); |
|
|
|
ClassDeclaration inner = cpool.getDeclaration(env, inner_index); |
|
|
|
// Read in the outer_class_info. Note that the index will be |
|
|
|
ClassDeclaration outer = null; |
|
|
|
int outer_index = ds.readUnsignedShort(); |
|
if (outer_index != 0) { |
|
outer = cpool.getDeclaration(env, outer_index); |
|
} |
|
|
|
// Read in the inner_name_index. This may be zero. An anonymous |
|
// class will either have an inner_nm_index of zero (as the spec |
|
// dictates) or it will have an inner_nm of idNull (for classes |
|
|
|
Identifier inner_nm = idNull; |
|
|
|
int inner_nm_index = ds.readUnsignedShort(); |
|
if (inner_nm_index != 0) { |
|
inner_nm = Identifier.lookup(cpool.getString(inner_nm_index)); |
|
} |
|
|
|
// Read in the modifiers for the inner class. |
|
|
|
int mods = ds.readUnsignedShort(); |
|
|
|
// Is the class accessible? |
|
// The old code checked for |
|
// |
|
// (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0) |
|
// |
|
// which we will preserve to keep it working for class files |
|
// generated by 1.1 compilers. In addition we check for |
|
// |
|
// (outer != null) |
|
// |
|
// as an additional check that only makes sense with 1.2 |
|
// generated files. Note that it is entirely possible that |
|
// the M_PRIVATE bit is always enough. We are being |
|
// conservative here. |
|
// |
|
// The ATT_ALLCLASSES flag causes the M_PRIVATE modifier |
|
// to be ignored, and is used by tools such as 'javap' that |
|
// wish to examine all classes regardless of the normal access |
|
// controls that apply during compilation. Note that anonymous |
|
// and local classes are still not considered accessible, though |
|
// named local classes in jdk1.1 may slip through. Note that |
|
// this accessibility test is an optimization, and it is safe to |
|
|
|
boolean accessible = |
|
(outer != null) && |
|
(!inner_nm.equals(idNull)) && |
|
((mods & M_PRIVATE) == 0 || |
|
(flags & ATT_ALLCLASSES) != 0); |
|
|
|
// The reader should note that there has been a significant change |
|
// in the way that the InnerClasses attribute is being handled. |
|
// In particular, previously the compiler called initInner() for |
|
// <em>every</em> inner class. Now the compiler does not call |
|
// initInner() if the inner class is inaccessible. This means |
|
// that inaccessible inner classes don't have any of the processing |
|
// from initInner() done for them: fixing the access flags, |
|
// setting outerClass, setting outerMember in their outerClass, |
|
// etc. We believe this is fine: if the class is inaccessible |
|
// and binary, then everyone who needs to see its internals |
|
// has already been compiled. Hopefully. |
|
|
|
if (accessible) { |
|
Identifier nm = |
|
Identifier.lookupInner(outer.getName(), inner_nm); |
|
|
|
|
|
Type.tClass(nm); |
|
|
|
if (inner.equals(getClassDeclaration())) { |
|
|
|
try { |
|
ClassDefinition outerClass = outer.getClassDefinition(env); |
|
initInner(outerClass, mods); |
|
} catch (ClassNotFound e) { |
|
// report the error elsewhere |
|
} |
|
} else if (outer.equals(getClassDeclaration())) { |
|
|
|
try { |
|
ClassDefinition innerClass = |
|
inner.getClassDefinition(env); |
|
initOuter(innerClass, mods); |
|
} catch (ClassNotFound e) { |
|
// report the error elsewhere |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
private void initInner(ClassDefinition outerClass, int mods) { |
|
if (getOuterClass() != null) |
|
return; |
|
/****** |
|
// Maybe set static, protected, or private. |
|
if ((modifiers & M_PUBLIC) != 0) |
|
mods &= M_STATIC; |
|
else |
|
mods &= M_PRIVATE | M_PROTECTED | M_STATIC; |
|
modifiers |= mods; |
|
******/ |
|
// For an inner class, the class access may have been weakened |
|
// from that originally declared the source. We must take the |
|
// actual access permissions against which we check any source |
|
// we are currently compiling from the InnerClasses attribute. |
|
|
|
if ((mods & M_PRIVATE) != 0) { |
|
|
|
mods &= ~(M_PUBLIC | M_PROTECTED); |
|
} else if ((mods & M_PROTECTED) != 0) { |
|
|
|
mods &= ~M_PUBLIC; |
|
} |
|
if ((mods & M_INTERFACE) != 0) { |
|
// All interfaces are implicitly abstract. |
|
|
|
mods |= (M_ABSTRACT | M_STATIC); |
|
} |
|
if (outerClass.isInterface()) { |
|
// All types that are members of interfaces are implicitly |
|
|
|
mods |= (M_PUBLIC | M_STATIC); |
|
mods &= ~(M_PRIVATE | M_PROTECTED); |
|
} |
|
modifiers = mods; |
|
|
|
setOuterClass(outerClass); |
|
|
|
for (MemberDefinition field = getFirstMember(); |
|
field != null; |
|
field = field.getNextMember()) { |
|
if (field.isUplevelValue() |
|
&& outerClass.getType().equals(field.getType()) |
|
&& field.getName().toString().startsWith(prefixThis)) { |
|
setOuterMember(field); |
|
} |
|
} |
|
} |
|
|
|
private void initOuter(ClassDefinition innerClass, int mods) { |
|
if (innerClass instanceof BinaryClass) |
|
((BinaryClass)innerClass).initInner(this, mods); |
|
addMember(new BinaryMember(innerClass)); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void write(Environment env, OutputStream out) throws IOException { |
|
DataOutputStream data = new DataOutputStream(out); |
|
|
|
|
|
data.writeInt(JAVA_MAGIC); |
|
data.writeShort(env.getMinorVersion()); |
|
data.writeShort(env.getMajorVersion()); |
|
|
|
|
|
cpool.write(data, env); |
|
|
|
|
|
data.writeShort(getModifiers() & ACCM_CLASS); |
|
data.writeShort(cpool.indexObject(getClassDeclaration(), env)); |
|
data.writeShort((getSuperClass() != null) |
|
? cpool.indexObject(getSuperClass(), env) : 0); |
|
data.writeShort(interfaces.length); |
|
for (int i = 0 ; i < interfaces.length ; i++) { |
|
data.writeShort(cpool.indexObject(interfaces[i], env)); |
|
} |
|
|
|
|
|
int fieldCount = 0, methodCount = 0; |
|
for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) |
|
if (f.isMethod()) methodCount++; else fieldCount++; |
|
|
|
|
|
data.writeShort(fieldCount); |
|
for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) { |
|
if (!f.isMethod()) { |
|
data.writeShort(f.getModifiers() & ACCM_FIELD); |
|
String name = f.getName().toString(); |
|
String signature = f.getType().getTypeSignature(); |
|
data.writeShort(cpool.indexString(name, env)); |
|
data.writeShort(cpool.indexString(signature, env)); |
|
BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env); |
|
} |
|
} |
|
|
|
|
|
data.writeShort(methodCount); |
|
for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) { |
|
if (f.isMethod()) { |
|
data.writeShort(f.getModifiers() & ACCM_METHOD); |
|
String name = f.getName().toString(); |
|
String signature = f.getType().getTypeSignature(); |
|
data.writeShort(cpool.indexString(name, env)); |
|
data.writeShort(cpool.indexString(signature, env)); |
|
BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env); |
|
} |
|
} |
|
|
|
|
|
BinaryAttribute.write(atts, data, cpool, env); |
|
data.flush(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Enumeration getDependencies() { |
|
return dependencies.elements(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addDependency(ClassDeclaration c) { |
|
if ((c != null) && !dependencies.contains(c)) { |
|
dependencies.addElement(c); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public BinaryConstantPool getConstants() { |
|
return cpool; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public byte getAttribute(Identifier name)[] { |
|
for (BinaryAttribute att = atts ; att != null ; att = att.next) { |
|
if (att.name.equals(name)) { |
|
return att.data; |
|
} |
|
} |
|
return null; |
|
} |
|
} |