001: /*
002: * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.java.util.jar.pack;
027:
028: import java.io.*;
029: import java.util.*;
030: import com.sun.java.util.jar.pack.Package.Class;
031: import com.sun.java.util.jar.pack.Package.InnerClass;
032: import com.sun.java.util.jar.pack.ConstantPool.*;
033:
034: /**
035: * Reader for a class file that is being incorporated into a package.
036: * @author John Rose
037: * @version 1.24, 05/05/07
038: */
039: class ClassReader implements Constants {
040: int verbose;
041:
042: Package pkg;
043: Class cls;
044: long inPos;
045: DataInputStream in;
046: Map attrDefs;
047: Map attrCommands;
048: String unknownAttrCommand = "error";;
049:
050: ClassReader(Class cls, InputStream in) throws IOException {
051: this .pkg = cls.getPackage();
052: this .cls = cls;
053: this .verbose = pkg.verbose;
054: this .in = new DataInputStream(new FilterInputStream(in) {
055: public int read(byte b[], int off, int len)
056: throws IOException {
057: int nr = super .read(b, off, len);
058: if (nr >= 0)
059: inPos += nr;
060: return nr;
061: }
062:
063: public int read() throws IOException {
064: int ch = super .read();
065: if (ch >= 0)
066: inPos += 1;
067: return ch;
068: }
069:
070: public long skip(long n) throws IOException {
071: long ns = super .skip(n);
072: if (ns >= 0)
073: inPos += ns;
074: return ns;
075: }
076: });
077: }
078:
079: public void setAttrDefs(Map attrDefs) {
080: this .attrDefs = attrDefs;
081: }
082:
083: public void setAttrCommands(Map attrCommands) {
084: this .attrCommands = attrCommands;
085: }
086:
087: private void skip(int n, String what) throws IOException {
088: Utils.log.warning("skipping " + n + " bytes of " + what);
089: long skipped = 0;
090: while (skipped < n) {
091: long j = in.skip(n - skipped);
092: assert (j > 0);
093: skipped += j;
094: }
095: assert (skipped == n);
096: }
097:
098: private int readUnsignedShort() throws IOException {
099: return in.readUnsignedShort();
100: }
101:
102: private int readInt() throws IOException {
103: return in.readInt();
104: }
105:
106: /** Read a 2-byte int, and return the <em>global</em> CP entry for it. */
107: private Entry readRef() throws IOException {
108: int i = in.readUnsignedShort();
109: return i == 0 ? null : cls.cpMap[i];
110: }
111:
112: private Entry readRef(byte tag) throws IOException {
113: Entry e = readRef();
114: assert (e != null);
115: assert (e.tagMatches(tag));
116: return e;
117: }
118:
119: private Entry readRefOrNull(byte tag) throws IOException {
120: Entry e = readRef();
121: assert (e == null || e.tagMatches(tag));
122: return e;
123: }
124:
125: private Utf8Entry readUtf8Ref() throws IOException {
126: return (Utf8Entry) readRef(CONSTANT_Utf8);
127: }
128:
129: private ClassEntry readClassRef() throws IOException {
130: return (ClassEntry) readRef(CONSTANT_Class);
131: }
132:
133: private ClassEntry readClassRefOrNull() throws IOException {
134: return (ClassEntry) readRefOrNull(CONSTANT_Class);
135: }
136:
137: private SignatureEntry readSignatureRef() throws IOException {
138: // The class file stores a Utf8, but we want a Signature.
139: Entry e = readRef(CONSTANT_Utf8);
140: return ConstantPool.getSignatureEntry(e.stringValue());
141: }
142:
143: void read() throws IOException {
144: boolean ok = false;
145: try {
146: readMagicNumbers();
147: readConstantPool();
148: readHeader();
149: readMembers(false); // fields
150: readMembers(true); // methods
151: readAttributes(ATTR_CONTEXT_CLASS, cls);
152: cls.finishReading();
153: assert (0 >= in.read(new byte[1]));
154: ok = true;
155: } finally {
156: if (!ok) {
157: if (verbose > 0)
158: Utils.log.warning("Erroneous data at input offset "
159: + inPos + " of " + cls.file);
160: }
161: }
162: }
163:
164: void readMagicNumbers() throws IOException {
165: cls.magic = in.readInt();
166: if (cls.magic != JAVA_MAGIC)
167: throw new Attribute.FormatException(
168: "Bad magic number in class file "
169: + Integer.toHexString(cls.magic),
170: ATTR_CONTEXT_CLASS, "magic-number", "pass");
171: cls.minver = (short) readUnsignedShort();
172: cls.majver = (short) readUnsignedShort();
173: //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver);
174: String bad = checkVersion(cls.majver, cls.minver);
175: if (bad != null) {
176: throw new Attribute.FormatException(
177: "classfile version too " + bad + ": " + cls.majver
178: + "." + cls.minver + " in " + cls.file,
179: ATTR_CONTEXT_CLASS, "version", "pass");
180: }
181: }
182:
183: private String checkVersion(int majver, int minver) {
184: if (majver < pkg.min_class_majver
185: || (majver == pkg.min_class_majver && minver < pkg.min_class_minver)) {
186: return "small";
187: }
188: if (majver > pkg.max_class_majver
189: || (majver == pkg.max_class_majver && minver > pkg.max_class_minver)) {
190: return "large";
191: }
192: return null; // OK
193: }
194:
195: void readConstantPool() throws IOException {
196: int length = in.readUnsignedShort();
197: //System.err.println("reading CP, length="+length);
198:
199: int[] fixups = new int[length * 4];
200: int fptr = 0;
201:
202: Entry[] cpMap = new Entry[length];
203: cpMap[0] = null;
204: for (int i = 1; i < length; i++) {
205: //System.err.println("reading CP elt, i="+i);
206: int tag = in.readByte();
207: switch (tag) {
208: case CONSTANT_Utf8:
209: cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF());
210: break;
211: case CONSTANT_Integer: {
212: Comparable val = new Integer(in.readInt());
213: cpMap[i] = ConstantPool.getLiteralEntry(val);
214: }
215: break;
216: case CONSTANT_Float: {
217: Comparable val = new Float(in.readFloat());
218: cpMap[i] = ConstantPool.getLiteralEntry(val);
219: }
220: break;
221: case CONSTANT_Long: {
222: Comparable val = new Long(in.readLong());
223: cpMap[i] = ConstantPool.getLiteralEntry(val);
224: cpMap[++i] = null;
225: }
226: break;
227: case CONSTANT_Double: {
228: Comparable val = new Double(in.readDouble());
229: cpMap[i] = ConstantPool.getLiteralEntry(val);
230: cpMap[++i] = null;
231: }
232: break;
233:
234: // just read the refs; do not attempt to resolve while reading
235: case CONSTANT_Class:
236: case CONSTANT_String:
237: fixups[fptr++] = i;
238: fixups[fptr++] = tag;
239: fixups[fptr++] = in.readUnsignedShort();
240: fixups[fptr++] = -1; // empty ref2
241: break;
242: case CONSTANT_Fieldref:
243: case CONSTANT_Methodref:
244: case CONSTANT_InterfaceMethodref:
245: case CONSTANT_NameandType:
246: fixups[fptr++] = i;
247: fixups[fptr++] = tag;
248: fixups[fptr++] = in.readUnsignedShort();
249: fixups[fptr++] = in.readUnsignedShort();
250: break;
251: default:
252: throw new IOException("Bad constant pool tag " + tag);
253: }
254: }
255:
256: // Fix up refs, which might be out of order.
257: while (fptr > 0) {
258: if (verbose > 3)
259: Utils.log.fine("CP fixups [" + fptr / 4 + "]");
260: int flimit = fptr;
261: fptr = 0;
262: for (int fi = 0; fi < flimit;) {
263: int cpi = fixups[fi++];
264: int tag = fixups[fi++];
265: int ref = fixups[fi++];
266: int ref2 = fixups[fi++];
267: if (verbose > 3)
268: Utils.log.fine(" cp[" + cpi + "] = "
269: + ConstantPool.tagName(tag) + "{" + ref
270: + "," + ref2 + "}");
271: if (cpMap[ref] == null || ref2 >= 0
272: && cpMap[ref2] == null) {
273: // Defer.
274: fixups[fptr++] = cpi;
275: fixups[fptr++] = tag;
276: fixups[fptr++] = ref;
277: fixups[fptr++] = ref2;
278: continue;
279: }
280: switch (tag) {
281: case CONSTANT_Class:
282: cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref]
283: .stringValue());
284: break;
285: case CONSTANT_String:
286: cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref]
287: .stringValue());
288: break;
289: case CONSTANT_Fieldref:
290: case CONSTANT_Methodref:
291: case CONSTANT_InterfaceMethodref:
292: ClassEntry mclass = (ClassEntry) cpMap[ref];
293: DescriptorEntry mdescr = (DescriptorEntry) cpMap[ref2];
294: cpMap[cpi] = ConstantPool.getMemberEntry(
295: (byte) tag, mclass, mdescr);
296: break;
297: case CONSTANT_NameandType:
298: Utf8Entry mname = (Utf8Entry) cpMap[ref];
299: Utf8Entry mtype = (Utf8Entry) cpMap[ref2];
300: cpMap[cpi] = ConstantPool.getDescriptorEntry(mname,
301: mtype);
302: break;
303: default:
304: assert (false);
305: }
306: }
307: assert (fptr < flimit); // Must make progress.
308: }
309:
310: cls.cpMap = cpMap;
311: }
312:
313: void readHeader() throws IOException {
314: cls.flags = readUnsignedShort();
315: cls.this Class = readClassRef();
316: cls.super Class = readClassRefOrNull();
317: int ni = readUnsignedShort();
318: cls.interfaces = new ClassEntry[ni];
319: for (int i = 0; i < ni; i++) {
320: cls.interfaces[i] = readClassRef();
321: }
322: }
323:
324: void readMembers(boolean doMethods) throws IOException {
325: int nm = readUnsignedShort();
326: for (int i = 0; i < nm; i++) {
327: readMember(doMethods);
328: }
329: }
330:
331: void readMember(boolean doMethod) throws IOException {
332: int mflags = readUnsignedShort();
333: Utf8Entry mname = readUtf8Ref();
334: SignatureEntry mtype = readSignatureRef();
335: DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname,
336: mtype);
337: Class.Member m;
338: if (!doMethod)
339: m = cls.new Field(mflags, descr);
340: else
341: m = cls.new Method(mflags, descr);
342: readAttributes(!doMethod ? ATTR_CONTEXT_FIELD
343: : ATTR_CONTEXT_METHOD, m);
344: }
345:
346: void readAttributes(int ctype, Attribute.Holder h)
347: throws IOException {
348: int na = readUnsignedShort();
349: if (na == 0)
350: return; // nothing to do here
351: if (verbose > 3)
352: Utils.log.fine("readAttributes " + h + " [" + na + "]");
353: for (int i = 0; i < na; i++) {
354: String name = readUtf8Ref().stringValue();
355: int length = readInt();
356: // See if there is a special command that applies.
357: if (attrCommands != null) {
358: Object lkey = Attribute.keyForLookup(ctype, name);
359: String cmd = (String) attrCommands.get(lkey);
360: if (cmd == "pass") {
361: String message = "passing attribute bitwise in "
362: + h;
363: throw new Attribute.FormatException(message, ctype,
364: name, cmd);
365: } else if (cmd == "error") {
366: String message = "attribute not allowed in " + h;
367: throw new Attribute.FormatException(message, ctype,
368: name, cmd);
369: } else if (cmd == "strip") {
370: skip(length, name + " attribute in " + h);
371: continue;
372: }
373: }
374: // Find canonical instance of the requested attribute.
375: Attribute a = Attribute.lookup(Package.attrDefs, ctype,
376: name);
377: if (verbose > 4 && a != null)
378: Utils.log.fine("pkg_attribute_lookup " + name + " = "
379: + a);
380: if (a == null) {
381: a = Attribute.lookup(this .attrDefs, ctype, name);
382: if (verbose > 4 && a != null)
383: Utils.log.fine("this " + name + " = " + a);
384: }
385: if (a == null) {
386: a = Attribute.lookup(null, ctype, name);
387: if (verbose > 4 && a != null)
388: Utils.log.fine("null_attribute_lookup " + name
389: + " = " + a);
390: }
391: if (a == null && length == 0) {
392: // Any zero-length attr is "known"...
393: // We can assume an empty attr. has an empty layout.
394: // Handles markers like Enum, Bridge, Synthetic, Deprecated.
395: a = Attribute.find(ctype, name, "");
396: }
397: boolean isStackMap = (ctype == ATTR_CONTEXT_CODE && (name
398: .equals("StackMap") || name.equals("StackMapX")));
399: if (isStackMap) {
400: // Known attribute but with a corner case format, "pass" it.
401: Code code = (Code) h;
402: final int TOO_BIG = 0x10000;
403: if (code.max_stack >= TOO_BIG
404: || code.max_locals >= TOO_BIG
405: || code.getLength() >= TOO_BIG
406: || name.endsWith("X")) {
407: // No, we don't really know what to do this this one.
408: // Do not compress the rare and strange "u4" and "X" cases.
409: a = null;
410: }
411: }
412: if (a == null) {
413: if (isStackMap) {
414: // Known attribute but w/o a format; pass it.
415: String message = "unsupported StackMap variant in "
416: + h;
417: throw new Attribute.FormatException(message, ctype,
418: name, "pass");
419: } else if (unknownAttrCommand == "strip") {
420: // Skip the unknown attribute.
421: skip(length, "unknown " + name + " attribute in "
422: + h);
423: continue;
424: } else {
425: String message = "unknown in " + h;
426: throw new Attribute.FormatException(message, ctype,
427: name, unknownAttrCommand);
428: }
429: }
430: if (a.layout() == Package.attrCodeEmpty
431: || a.layout() == Package.attrInnerClassesEmpty) {
432: // These are hardwired.
433: long pos0 = inPos;
434: if (a.name() == "Code") {
435: Class.Method m = (Class.Method) h;
436: m.code = new Code(m);
437: readCode(m.code);
438: } else {
439: assert (h == cls);
440: readInnerClasses(cls);
441: }
442: assert (length == inPos - pos0);
443: // Keep empty attribute a...
444: } else if (length > 0) {
445: byte[] bytes = new byte[length];
446: in.readFully(bytes);
447: a = a.addContent(bytes);
448: }
449: h.addAttribute(a);
450: if (verbose > 2)
451: Utils.log.fine("read " + a);
452: }
453: }
454:
455: void readCode(Code code) throws IOException {
456: code.max_stack = readUnsignedShort();
457: code.max_locals = readUnsignedShort();
458: code.bytes = new byte[readInt()];
459: in.readFully(code.bytes);
460: int nh = readUnsignedShort();
461: code.setHandlerCount(nh);
462: for (int i = 0; i < nh; i++) {
463: code.handler_start[i] = readUnsignedShort();
464: code.handler_end[i] = readUnsignedShort();
465: code.handler_catch[i] = readUnsignedShort();
466: code.handler_class[i] = readClassRefOrNull();
467: }
468: readAttributes(ATTR_CONTEXT_CODE, code);
469: }
470:
471: void readInnerClasses(Class cls) throws IOException {
472: int nc = readUnsignedShort();
473: ArrayList ics = new ArrayList(nc);
474: for (int i = 0; i < nc; i++) {
475: InnerClass ic = new InnerClass(readClassRef(),
476: readClassRefOrNull(),
477: (Utf8Entry) readRefOrNull(CONSTANT_Utf8),
478: readUnsignedShort());
479: ics.add(ic);
480: }
481: cls.innerClasses = ics; // set directly; do not use setInnerClasses.
482: // (Later, ics may be transferred to the pkg.)
483: }
484: }
|