blob: 3737da6bac2f16cccc20e91f137617538c26b583 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.tools.java;
27
28import java.io.IOException;
29import java.io.DataInputStream;
30import java.io.OutputStream;
31import java.io.DataOutputStream;
32import java.io.ByteArrayInputStream;
33import java.util.Hashtable;
34import java.util.Vector;
35import java.util.Enumeration;
36
37/**
38 * WARNING: The contents of this source file are not part of any
39 * supported API. Code that depends on them does so at its own risk:
40 * they are subject to change or removal without notice.
41 */
42public final
43class BinaryClass extends ClassDefinition implements Constants {
44 BinaryConstantPool cpool;
45 BinaryAttribute atts;
46 Vector dependencies;
47 private boolean haveLoadedNested = false;
48
49 /**
50 * Constructor
51 */
52 public BinaryClass(Object source, ClassDeclaration declaration, int modifiers,
53 ClassDeclaration superClass, ClassDeclaration interfaces[],
54 Vector dependencies) {
55 super(source, 0, declaration, modifiers, null, null);
56 this.dependencies = dependencies;
57 this.superClass = superClass;
58 this.interfaces = interfaces;
59 }
60
61 /**
62 * Flags used by basicCheck() to avoid duplicate calls.
63 * (Part of fix for 4105911)
64 */
65 private boolean basicCheckDone = false;
66 private boolean basicChecking = false;
67
68 /**
69 * Ready a BinaryClass for further checking. Note that, until recently,
70 * BinaryClass relied on the default basicCheck() provided by
71 * ClassDefinition. The definition here has been added to ensure that
72 * the information generated by collectInheritedMethods is available
73 * for BinaryClasses.
74 */
75 protected void basicCheck(Environment env) throws ClassNotFound {
76 if (tracing) env.dtEnter("BinaryClass.basicCheck: " + getName());
77
78 // We need to guard against duplicate calls to basicCheck(). They
79 // can lead to calling collectInheritedMethods() for this class
80 // from within a previous call to collectInheritedMethods() for
81 // this class. That is not allowed.
82 // (Part of fix for 4105911)
83 if (basicChecking || basicCheckDone) {
84 if (tracing) env.dtExit("BinaryClass.basicCheck: OK " + getName());
85 return;
86 }
87
88 if (tracing) env.dtEvent("BinaryClass.basicCheck: CHECKING " + getName());
89 basicChecking = true;
90
91 super.basicCheck(env);
92
93 // Collect inheritance information.
94 if (doInheritanceChecks) {
95 collectInheritedMethods(env);
96 }
97
98 basicCheckDone = true;
99 basicChecking = false;
100 if (tracing) env.dtExit("BinaryClass.basicCheck: " + getName());
101 }
102
103 /**
104 * Load a binary class
105 */
106 public static BinaryClass load(Environment env, DataInputStream in) throws IOException {
107 return load(env, in, ~(ATT_CODE|ATT_ALLCLASSES));
108 }
109
110 public static BinaryClass load(Environment env,
111 DataInputStream in, int mask) throws IOException {
112 // Read the header
113 int magic = in.readInt(); // JVM 4.1 ClassFile.magic
114 if (magic != JAVA_MAGIC) {
115 throw new ClassFormatError("wrong magic: " + magic + ", expected " + JAVA_MAGIC);
116 }
117 int minor_version = in.readUnsignedShort(); // JVM 4.1 ClassFile.minor_version
118 int version = in.readUnsignedShort(); // JVM 4.1 ClassFile.major_version
119 if (version < JAVA_MIN_SUPPORTED_VERSION) {
120 throw new ClassFormatError(
121 sun.tools.javac.Main.getText(
122 "javac.err.version.too.old",
123 String.valueOf(version)));
124 } else if ((version > JAVA_MAX_SUPPORTED_VERSION)
125 || (version == JAVA_MAX_SUPPORTED_VERSION
126 && minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) {
127 throw new ClassFormatError(
128 sun.tools.javac.Main.getText(
129 "javac.err.version.too.recent",
130 version+"."+minor_version));
131 }
132
133 // Read the constant pool
134 BinaryConstantPool cpool = new BinaryConstantPool(in);
135
136 // The dependencies of this class
137 Vector dependencies = cpool.getDependencies(env);
138
139 // Read modifiers
140 int classMod = in.readUnsignedShort() & ACCM_CLASS; // JVM 4.1 ClassFile.access_flags
141
142 // Read the class name - from JVM 4.1 ClassFile.this_class
143 ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort());
144
145 // Read the super class name (may be null) - from JVM 4.1 ClassFile.super_class
146 ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort());
147
148 // Read the interface names - from JVM 4.1 ClassFile.interfaces_count
149 ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()];
150 for (int i = 0 ; i < interfaces.length ; i++) {
151 // JVM 4.1 ClassFile.interfaces[]
152 interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort());
153 }
154
155 // Allocate the class
156 BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl,
157 interfaces, dependencies);
158 c.cpool = cpool;
159
160 // Add any additional dependencies
161 c.addDependency(superClassDecl);
162
163 // Read the fields
164 int nfields = in.readUnsignedShort(); // JVM 4.1 ClassFile.fields_count
165 for (int i = 0 ; i < nfields ; i++) {
166 // JVM 4.5 field_info.access_flags
167 int fieldMod = in.readUnsignedShort() & ACCM_FIELD;
168 // JVM 4.5 field_info.name_index
169 Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort());
170 // JVM 4.5 field_info.descriptor_index
171 Type fieldType = cpool.getType(in.readUnsignedShort());
172 BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
173 c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts));
174 }
175
176 // Read the methods
177 int nmethods = in.readUnsignedShort(); // JVM 4.1 ClassFile.methods_count
178 for (int i = 0 ; i < nmethods ; i++) {
179 // JVM 4.6 method_info.access_flags
180 int methMod = in.readUnsignedShort() & ACCM_METHOD;
181 // JVM 4.6 method_info.name_index
182 Identifier methName = cpool.getIdentifier(in.readUnsignedShort());
183 // JVM 4.6 method_info.descriptor_index
184 Type methType = cpool.getType(in.readUnsignedShort());
185 BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
186 c.addMember(new BinaryMember(c, methMod, methType, methName, atts));
187 }
188
189 // Read the class attributes
190 c.atts = BinaryAttribute.load(in, cpool, mask);
191
192 // See if the SourceFile is known
193 byte data[] = c.getAttribute(idSourceFile);
194 if (data != null) {
195 DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data));
196 // JVM 4.7.2 SourceFile_attribute.sourcefile_index
197 c.source = cpool.getString(dataStream.readUnsignedShort());
198 }
199
200 // See if the Documentation is know
201 data = c.getAttribute(idDocumentation);
202 if (data != null) {
203 c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF();
204 }
205
206 // Was it compiled as deprecated?
207 if (c.getAttribute(idDeprecated) != null) {
208 c.modifiers |= M_DEPRECATED;
209 }
210
211 // Was it synthesized by the compiler?
212 if (c.getAttribute(idSynthetic) != null) {
213 c.modifiers |= M_SYNTHETIC;
214 }
215
216 return c;
217 }
218
219 /**
220 * Called when an environment ties a binary definition to a declaration.
221 * At this point, auxiliary definitions may be loaded.
222 */
223
224 public void loadNested(Environment env) {
225 loadNested(env, 0);
226 }
227
228 public void loadNested(Environment env, int flags) {
229 // Sanity check.
230 if (haveLoadedNested) {
231 // Duplicate calls most likely should not occur, but they do
232 // in javap. Be tolerant of them for the time being.
233 // throw new CompilerError("multiple loadNested");
234 if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED");
235 return;
236 }
237 haveLoadedNested = true;
238 // Read class-nesting information.
239 try {
240 byte data[];
241 data = getAttribute(idInnerClasses);
242 if (data != null) {
243 initInnerClasses(env, data, flags);
244 }
245 } catch (IOException ee) {
246 // The inner classes attribute is not well-formed.
247 // It may, for example, contain no data. Report this.
248 // We used to throw a CompilerError here (bug 4095108).
249 env.error(0, "malformed.attribute", getClassDeclaration(),
250 idInnerClasses);
251 if (tracing)
252 env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)");
253 }
254 }
255
256 private void initInnerClasses(Environment env,
257 byte data[],
258 int flags) throws IOException {
259 DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
260 int nrec = ds.readUnsignedShort(); // InnerClasses_attribute.number_of_classes
261 for (int i = 0; i < nrec; i++) {
262 // For each inner class name transformation, we have a record
263 // with the following fields:
264 //
265 // u2 inner_class_info_index; // CONSTANT_Class_info index
266 // u2 outer_class_info_index; // CONSTANT_Class_info index
267 // u2 inner_name_index; // CONSTANT_Utf8_info index
268 // u2 inner_class_access_flags; // access_flags bitmask
269 //
270 // The spec states that outer_class_info_index is 0 iff
271 // the inner class is not a member of its enclosing class (i.e.
272 // it is a local or anonymous class). The spec also states
273 // that if a class is anonymous then inner_name_index should
274 // be 0.
275 //
276 // Prior to jdk1.2, javac did not implement the spec. Instead
277 // it <em>always</em> set outer_class_info_index to the
278 // enclosing outer class and if the class was anonymous,
279 // it set inner_name_index to be the index of a CONSTANT_Utf8
280 // entry containing the null string "" (idNull). This code is
281 // designed to handle either kind of class file.
282 //
283 // See also the compileClass() method in SourceClass.java.
284
285 // Read in the inner_class_info
286 // InnerClasses_attribute.classes.inner_class_info_index
287 int inner_index = ds.readUnsignedShort();
288 // could check for zero.
289 ClassDeclaration inner = cpool.getDeclaration(env, inner_index);
290
291 // Read in the outer_class_info. Note that the index will be
292 // zero if the class is "not a member".
293 ClassDeclaration outer = null;
294 // InnerClasses_attribute.classes.outer_class_info_index
295 int outer_index = ds.readUnsignedShort();
296 if (outer_index != 0) {
297 outer = cpool.getDeclaration(env, outer_index);
298 }
299
300 // Read in the inner_name_index. This may be zero. An anonymous
301 // class will either have an inner_nm_index of zero (as the spec
302 // dictates) or it will have an inner_nm of idNull (for classes
303 // generated by pre-1.2 compilers). Handle both.
304 Identifier inner_nm = idNull;
305 // InnerClasses_attribute.classes.inner_name_index
306 int inner_nm_index = ds.readUnsignedShort();
307 if (inner_nm_index != 0) {
308 inner_nm = Identifier.lookup(cpool.getString(inner_nm_index));
309 }
310
311 // Read in the modifiers for the inner class.
312 // InnerClasses_attribute.classes.inner_name_index
313 int mods = ds.readUnsignedShort();
314
315 // Is the class accessible?
316 // The old code checked for
317 //
318 // (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0)
319 //
320 // which we will preserve to keep it working for class files
321 // generated by 1.1 compilers. In addition we check for
322 //
323 // (outer != null)
324 //
325 // as an additional check that only makes sense with 1.2
326 // generated files. Note that it is entirely possible that
327 // the M_PRIVATE bit is always enough. We are being
328 // conservative here.
329 //
330 // The ATT_ALLCLASSES flag causes the M_PRIVATE modifier
331 // to be ignored, and is used by tools such as 'javap' that
332 // wish to examine all classes regardless of the normal access
333 // controls that apply during compilation. Note that anonymous
334 // and local classes are still not considered accessible, though
335 // named local classes in jdk1.1 may slip through. Note that
336 // this accessibility test is an optimization, and it is safe to
337 // err on the side of greater accessibility.
338 boolean accessible =
339 (outer != null) &&
340 (!inner_nm.equals(idNull)) &&
341 ((mods & M_PRIVATE) == 0 ||
342 (flags & ATT_ALLCLASSES) != 0);
343
344 // The reader should note that there has been a significant change
345 // in the way that the InnerClasses attribute is being handled.
346 // In particular, previously the compiler called initInner() for
347 // <em>every</em> inner class. Now the compiler does not call
348 // initInner() if the inner class is inaccessible. This means
349 // that inaccessible inner classes don't have any of the processing
350 // from initInner() done for them: fixing the access flags,
351 // setting outerClass, setting outerMember in their outerClass,
352 // etc. We believe this is fine: if the class is inaccessible
353 // and binary, then everyone who needs to see its internals
354 // has already been compiled. Hopefully.
355
356 if (accessible) {
357 Identifier nm =
358 Identifier.lookupInner(outer.getName(), inner_nm);
359
360 // Tell the type module about the nesting relation:
361 Type.tClass(nm);
362
363 if (inner.equals(getClassDeclaration())) {
364 // The inner class in the record is this class.
365 try {
366 ClassDefinition outerClass = outer.getClassDefinition(env);
367 initInner(outerClass, mods);
368 } catch (ClassNotFound e) {
369 // report the error elsewhere
370 }
371 } else if (outer.equals(getClassDeclaration())) {
372 // The outer class in the record is this class.
373 try {
374 ClassDefinition innerClass =
375 inner.getClassDefinition(env);
376 initOuter(innerClass, mods);
377 } catch (ClassNotFound e) {
378 // report the error elsewhere
379 }
380 }
381 }
382 }
383 }
384
385 private void initInner(ClassDefinition outerClass, int mods) {
386 if (getOuterClass() != null)
387 return; // already done
388 /******
389 // Maybe set static, protected, or private.
390 if ((modifiers & M_PUBLIC) != 0)
391 mods &= M_STATIC;
392 else
393 mods &= M_PRIVATE | M_PROTECTED | M_STATIC;
394 modifiers |= mods;
395 ******/
396 // For an inner class, the class access may have been weakened
397 // from that originally declared the source. We must take the
398 // actual access permissions against which we check any source
399 // we are currently compiling from the InnerClasses attribute.
400 // We attempt to guard here against bogus combinations of modifiers.
401 if ((mods & M_PRIVATE) != 0) {
402 // Private cannot be combined with public or protected.
403 mods &= ~(M_PUBLIC | M_PROTECTED);
404 } else if ((mods & M_PROTECTED) != 0) {
405 // Protected cannot be combined with public.
406 mods &= ~M_PUBLIC;
407 }
408 if ((mods & M_INTERFACE) != 0) {
409 // All interfaces are implicitly abstract.
410 // All interfaces that are members of a type are implicitly static.
411 mods |= (M_ABSTRACT | M_STATIC);
412 }
413 if (outerClass.isInterface()) {
414 // All types that are members of interfaces are implicitly
415 // public and static.
416 mods |= (M_PUBLIC | M_STATIC);
417 mods &= ~(M_PRIVATE | M_PROTECTED);
418 }
419 modifiers = mods;
420
421 setOuterClass(outerClass);
422
423 for (MemberDefinition field = getFirstMember();
424 field != null;
425 field = field.getNextMember()) {
426 if (field.isUplevelValue()
427 && outerClass.getType().equals(field.getType())
428 && field.getName().toString().startsWith(prefixThis)) {
429 setOuterMember(field);
430 }
431 }
432 }
433
434 private void initOuter(ClassDefinition innerClass, int mods) {
435 if (innerClass instanceof BinaryClass)
436 ((BinaryClass)innerClass).initInner(this, mods);
437 addMember(new BinaryMember(innerClass));
438 }
439
440 /**
441 * Write the class out to a given stream. This function mirrors the loader.
442 */
443 public void write(Environment env, OutputStream out) throws IOException {
444 DataOutputStream data = new DataOutputStream(out);
445
446 // write out the header
447 data.writeInt(JAVA_MAGIC);
448 data.writeShort(env.getMinorVersion());
449 data.writeShort(env.getMajorVersion());
450
451 // Write out the constant pool
452 cpool.write(data, env);
453
454 // Write class information
455 data.writeShort(getModifiers() & ACCM_CLASS);
456 data.writeShort(cpool.indexObject(getClassDeclaration(), env));
457 data.writeShort((getSuperClass() != null)
458 ? cpool.indexObject(getSuperClass(), env) : 0);
459 data.writeShort(interfaces.length);
460 for (int i = 0 ; i < interfaces.length ; i++) {
461 data.writeShort(cpool.indexObject(interfaces[i], env));
462 }
463
464 // count the fields and the methods
465 int fieldCount = 0, methodCount = 0;
466 for (MemberDefinition f = firstMember; f != null; f = f.getNextMember())
467 if (f.isMethod()) methodCount++; else fieldCount++;
468
469 // write out each the field count, and then each field
470 data.writeShort(fieldCount);
471 for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
472 if (!f.isMethod()) {
473 data.writeShort(f.getModifiers() & ACCM_FIELD);
474 String name = f.getName().toString();
475 String signature = f.getType().getTypeSignature();
476 data.writeShort(cpool.indexString(name, env));
477 data.writeShort(cpool.indexString(signature, env));
478 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
479 }
480 }
481
482 // write out each method count, and then each method
483 data.writeShort(methodCount);
484 for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
485 if (f.isMethod()) {
486 data.writeShort(f.getModifiers() & ACCM_METHOD);
487 String name = f.getName().toString();
488 String signature = f.getType().getTypeSignature();
489 data.writeShort(cpool.indexString(name, env));
490 data.writeShort(cpool.indexString(signature, env));
491 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
492 }
493 }
494
495 // write out the class attributes
496 BinaryAttribute.write(atts, data, cpool, env);
497 data.flush();
498 }
499
500 /**
501 * Get the dependencies
502 */
503 public Enumeration getDependencies() {
504 return dependencies.elements();
505 }
506
507 /**
508 * Add a dependency
509 */
510 public void addDependency(ClassDeclaration c) {
511 if ((c != null) && !dependencies.contains(c)) {
512 dependencies.addElement(c);
513 }
514 }
515
516 /**
517 * Get the constant pool
518 */
519 public BinaryConstantPool getConstants() {
520 return cpool;
521 }
522
523 /**
524 * Get a class attribute
525 */
526 public byte getAttribute(Identifier name)[] {
527 for (BinaryAttribute att = atts ; att != null ; att = att.next) {
528 if (att.name.equals(name)) {
529 return att.data;
530 }
531 }
532 return null;
533 }
534}