0001: /*
0002: * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.java.util.jar.pack;
0027:
0028: import java.io.*;
0029: import java.util.*;
0030: import java.util.logging.Level;
0031: import com.sun.java.util.jar.pack.Package.Class;
0032: import com.sun.java.util.jar.pack.Package.File;
0033: import com.sun.java.util.jar.pack.Package.InnerClass;
0034: import com.sun.java.util.jar.pack.ConstantPool.*;
0035:
0036: /**
0037: * Writer for a package file.
0038: * @author John Rose
0039: * @version 1.33, 05/05/07
0040: */
0041: class PackageWriter extends BandStructure {
0042: Package pkg;
0043: OutputStream finalOut;
0044:
0045: PackageWriter(Package pkg, OutputStream out) throws IOException {
0046: this .pkg = pkg;
0047: this .finalOut = out;
0048: // Caller has specified archive version in the package:
0049: initPackageMajver(pkg.package_majver);
0050: }
0051:
0052: void write() throws IOException {
0053: boolean ok = false;
0054: try {
0055: if (verbose > 0) {
0056: Utils.log.info("Setting up constant pool...");
0057: }
0058: setup();
0059:
0060: if (verbose > 0) {
0061: Utils.log.info("Packing...");
0062: }
0063:
0064: // writeFileHeader() is done last, since it has ultimate counts
0065: // writeBandHeaders() is called after all other bands are done
0066: writeConstantPool();
0067: writeFiles();
0068: writeAttrDefs();
0069: writeInnerClasses();
0070: writeClassesAndByteCodes();
0071: writeAttrCounts();
0072:
0073: if (verbose > 1)
0074: printCodeHist();
0075:
0076: // choose codings (fill band_headers if needed)
0077: if (verbose > 0) {
0078: Utils.log.info("Coding...");
0079: }
0080: all_bands.chooseBandCodings();
0081:
0082: // now we can write the headers:
0083: writeFileHeader();
0084:
0085: writeAllBandsTo(finalOut);
0086:
0087: ok = true;
0088: } catch (Exception ee) {
0089: Utils.log.log(Level.WARNING, "Error on output: " + ee, ee);
0090: //if (verbose > 0) ee.printStackTrace();
0091: // Write partial output only if we are verbose.
0092: if (verbose > 0)
0093: finalOut.close();
0094: if (ee instanceof IOException)
0095: throw (IOException) ee;
0096: if (ee instanceof RuntimeException)
0097: throw (RuntimeException) ee;
0098: throw new Error("error packing", ee);
0099: }
0100: }
0101:
0102: HashSet requiredEntries; // for the CP
0103: HashMap backCountTable; // for layout callables
0104: int[][] attrCounts; // count attr. occurences
0105:
0106: void setup() {
0107: requiredEntries = new HashSet();
0108: setArchiveOptions();
0109: trimClassAttributes();
0110: collectAttributeLayouts();
0111: pkg.buildGlobalConstantPool(requiredEntries);
0112: setBandIndexes();
0113: makeNewAttributeBands();
0114: collectInnerClasses();
0115: }
0116:
0117: void setArchiveOptions() {
0118: // Decide on some archive options early.
0119: // Does not decide on: AO_HAVE_SPECIAL_FORMATS,
0120: // AO_HAVE_CP_NUMBERS, AO_HAVE_FILE_HEADERS.
0121: // Also, AO_HAVE_FILE_OPTIONS may be forced on later.
0122: int minModtime = pkg.default_modtime;
0123: int maxModtime = pkg.default_modtime;
0124: int minOptions = -1;
0125: int maxOptions = 0;
0126:
0127: // Import defaults from package (deflate hint, etc.).
0128: archiveOptions |= pkg.default_options;
0129:
0130: for (Iterator i = pkg.files.iterator(); i.hasNext();) {
0131: File file = (File) i.next();
0132:
0133: int modtime = file.modtime;
0134: int options = file.options;
0135:
0136: if (minModtime == NO_MODTIME) {
0137: minModtime = maxModtime = modtime;
0138: } else {
0139: if (minModtime > modtime)
0140: minModtime = modtime;
0141: if (maxModtime < modtime)
0142: maxModtime = modtime;
0143: }
0144: minOptions &= options;
0145: maxOptions |= options;
0146: }
0147: if (pkg.default_modtime == NO_MODTIME) {
0148: // Make everything else be a positive offset from here.
0149: pkg.default_modtime = minModtime;
0150: }
0151: if (minModtime != NO_MODTIME && minModtime != maxModtime) {
0152: // Put them into a band.
0153: archiveOptions |= AO_HAVE_FILE_MODTIME;
0154: }
0155: // If the archive deflation is set do not bother with each file.
0156: if (!testBit(archiveOptions, AO_DEFLATE_HINT)
0157: && minOptions != -1) {
0158: if (testBit(minOptions, FO_DEFLATE_HINT)) {
0159: // Every file has the deflate_hint set.
0160: // Set it for the whole archive, and omit options.
0161: archiveOptions |= AO_DEFLATE_HINT;
0162: minOptions -= FO_DEFLATE_HINT;
0163: maxOptions -= FO_DEFLATE_HINT;
0164: }
0165: pkg.default_options |= minOptions;
0166: if (minOptions != maxOptions
0167: || minOptions != pkg.default_options) {
0168: archiveOptions |= AO_HAVE_FILE_OPTIONS;
0169: }
0170: }
0171: // Decide on default version number (majority rule).
0172: HashMap verCounts = new HashMap();
0173: int bestCount = 0;
0174: int bestVersion = -1;
0175: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
0176: Class cls = (Class) i.next();
0177: int version = cls.getVersion();
0178: int[] var = (int[]) verCounts.get(new Integer(version));
0179: if (var == null) {
0180: var = new int[1];
0181: verCounts.put(new Integer(version), var);
0182: }
0183: int count = (var[0] += 1);
0184: //System.out.println("version="+version+" count="+count);
0185: if (bestCount < count) {
0186: bestCount = count;
0187: bestVersion = version;
0188: }
0189: }
0190: verCounts.clear();
0191: if (bestVersion == -1)
0192: bestVersion = 0; // degenerate case
0193: int bestMajver = (char) (bestVersion >>> 16);
0194: int bestMinver = (char) (bestVersion);
0195: pkg.default_class_majver = (short) bestMajver;
0196: pkg.default_class_minver = (short) bestMinver;
0197: String bestVerStr = Package.versionStringOf(bestMajver,
0198: bestMinver);
0199: if (verbose > 0)
0200: Utils.log.info("Consensus version number in segment is "
0201: + bestVerStr);
0202: if (verbose > 0)
0203: Utils.log.info("Highest version number in segment is "
0204: + Package.versionStringOf(pkg
0205: .getHighestClassVersion()));
0206:
0207: // Now add explicit pseudo-attrs. to classes with odd versions.
0208: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
0209: Class cls = (Class) i.next();
0210:
0211: if (cls.getVersion() != bestVersion) {
0212: Attribute a = makeClassFileVersionAttr(cls.minver,
0213: cls.majver);
0214: if (verbose > 1) {
0215: String clsVer = cls.getVersionString();
0216: String pkgVer = bestVerStr;
0217: Utils.log.fine("Version " + clsVer + " of " + cls
0218: + " doesn't match package version "
0219: + pkgVer);
0220: }
0221: // Note: Does not add in "natural" order. (Who cares?)
0222: cls.addAttribute(a);
0223: }
0224: }
0225:
0226: // Decide if we are transmitting a huge resource file:
0227: for (Iterator i = pkg.files.iterator(); i.hasNext();) {
0228: File file = (File) i.next();
0229: long len = file.getFileLength();
0230: if (len != (int) len) {
0231: archiveOptions |= AO_HAVE_FILE_SIZE_HI;
0232: if (verbose > 0)
0233: Utils.log.info("Note: Huge resource file "
0234: + file.getFileName()
0235: + " forces 64-bit sizing");
0236: break;
0237: }
0238: }
0239:
0240: // Decide if code attributes typically have sub-attributes.
0241: // In that case, to preserve compact 1-byte code headers,
0242: // we must declare unconditional presence of code flags.
0243: int cost0 = 0;
0244: int cost1 = 0;
0245: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
0246: Class cls = (Class) i.next();
0247: for (Iterator j = cls.getMethods().iterator(); j.hasNext();) {
0248: Class.Method m = (Class.Method) j.next();
0249: if (m.code != null) {
0250: if (m.code.attributeSize() == 0) {
0251: // cost of a useless unconditional flags byte
0252: cost1 += 1;
0253: } else if (shortCodeHeader(m.code) != LONG_CODE_HEADER) {
0254: // cost of inflating a short header
0255: cost0 += 3;
0256: }
0257: }
0258: }
0259: }
0260: if (cost0 > cost1) {
0261: archiveOptions |= AO_HAVE_ALL_CODE_FLAGS;
0262: }
0263: if (verbose > 0)
0264: Utils.log.info("archiveOptions = " + "0b"
0265: + Integer.toBinaryString(archiveOptions));
0266: }
0267:
0268: void writeFileHeader() throws IOException {
0269: pkg.checkVersion();
0270: writeArchiveMagic();
0271: writeArchiveHeader();
0272: }
0273:
0274: // Local routine used to format fixed-format scalars
0275: // in the file_header:
0276: private void putMagicInt32(int val) throws IOException {
0277: int res = val;
0278: for (int i = 0; i < 4; i++) {
0279: archive_magic.putByte(0xFF & (res >>> 24));
0280: res <<= 8;
0281: }
0282: }
0283:
0284: void writeArchiveMagic() throws IOException {
0285: putMagicInt32(pkg.magic);
0286: }
0287:
0288: void writeArchiveHeader() throws IOException {
0289: // for debug only: number of words optimized away
0290: int headerDiscountForDebug = 0;
0291:
0292: // AO_HAVE_SPECIAL_FORMATS is set if non-default
0293: // coding techniques are used, or if there are
0294: // compressor-defined attributes transmitted.
0295: boolean haveSpecial = testBit(archiveOptions,
0296: AO_HAVE_SPECIAL_FORMATS);
0297: if (!haveSpecial) {
0298: haveSpecial |= (band_headers.length() != 0);
0299: haveSpecial |= (attrDefsWritten.length != 0);
0300: if (haveSpecial)
0301: archiveOptions |= AO_HAVE_SPECIAL_FORMATS;
0302: }
0303: if (!haveSpecial)
0304: headerDiscountForDebug += AH_SPECIAL_FORMAT_LEN;
0305:
0306: // AO_HAVE_FILE_HEADERS is set if there is any
0307: // file or segment envelope information present.
0308: boolean haveFiles = testBit(archiveOptions,
0309: AO_HAVE_FILE_HEADERS);
0310: if (!haveFiles) {
0311: haveFiles |= (archiveNextCount > 0);
0312: haveFiles |= (pkg.default_modtime != NO_MODTIME);
0313: if (haveFiles)
0314: archiveOptions |= AO_HAVE_FILE_HEADERS;
0315: }
0316: if (!haveFiles)
0317: headerDiscountForDebug += AH_FILE_HEADER_LEN;
0318:
0319: // AO_HAVE_CP_NUMBERS is set if there are any numbers
0320: // in the global constant pool. (Numbers are in 15% of classes.)
0321: boolean haveNumbers = testBit(archiveOptions,
0322: AO_HAVE_CP_NUMBERS);
0323: if (!haveNumbers) {
0324: haveNumbers |= pkg.cp.haveNumbers();
0325: if (haveNumbers)
0326: archiveOptions |= AO_HAVE_CP_NUMBERS;
0327: }
0328: if (!haveNumbers)
0329: headerDiscountForDebug += AH_CP_NUMBER_LEN;
0330:
0331: assert (pkg.package_majver > 0); // caller must specify!
0332: archive_header_0.putInt(pkg.package_minver);
0333: archive_header_0.putInt(pkg.package_majver);
0334: if (verbose > 0)
0335: Utils.log.info("Package Version for this segment:"
0336: + Package.versionStringOf(pkg.getPackageVersion()));
0337: archive_header_0.putInt(archiveOptions); // controls header format
0338: assert (archive_header_0.length() == AH_LENGTH_0);
0339:
0340: final int DUMMY = 0;
0341: if (haveFiles) {
0342: assert (archive_header_S.length() == AH_ARCHIVE_SIZE_HI);
0343: archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 32)
0344: assert (archive_header_S.length() == AH_ARCHIVE_SIZE_LO);
0345: archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 0)
0346: assert (archive_header_S.length() == AH_LENGTH_S);
0347: }
0348:
0349: // Done with unsized part of header....
0350:
0351: if (haveFiles) {
0352: archive_header_1.putInt(archiveNextCount); // usually zero
0353: archive_header_1.putInt(pkg.default_modtime);
0354: archive_header_1.putInt(pkg.files.size());
0355: } else {
0356: assert (pkg.files.size() == 0);
0357: }
0358:
0359: if (haveSpecial) {
0360: archive_header_1.putInt(band_headers.length());
0361: archive_header_1.putInt(attrDefsWritten.length);
0362: } else {
0363: assert (band_headers.length() == 0);
0364: assert (attrDefsWritten.length == 0);
0365: }
0366:
0367: writeConstantPoolCounts(haveNumbers);
0368:
0369: archive_header_1.putInt(pkg.getAllInnerClasses().size());
0370: archive_header_1.putInt(pkg.default_class_minver);
0371: archive_header_1.putInt(pkg.default_class_majver);
0372: archive_header_1.putInt(pkg.classes.size());
0373:
0374: // Sanity: Make sure we came out to 26 (less optional fields):
0375: assert (archive_header_0.length() + archive_header_S.length()
0376: + archive_header_1.length() == AH_LENGTH
0377: - headerDiscountForDebug);
0378:
0379: // Figure out all the sizes now, first cut:
0380: archiveSize0 = 0;
0381: archiveSize1 = all_bands.outputSize();
0382: // Second cut:
0383: archiveSize0 += archive_magic.outputSize();
0384: archiveSize0 += archive_header_0.outputSize();
0385: archiveSize0 += archive_header_S.outputSize();
0386: // Make the adjustments:
0387: archiveSize1 -= archiveSize0;
0388:
0389: // Patch the header:
0390: if (haveFiles) {
0391: int archiveSizeHi = (int) (archiveSize1 >>> 32);
0392: int archiveSizeLo = (int) (archiveSize1 >>> 0);
0393: archive_header_S.patchValue(AH_ARCHIVE_SIZE_HI,
0394: archiveSizeHi);
0395: archive_header_S.patchValue(AH_ARCHIVE_SIZE_LO,
0396: archiveSizeLo);
0397: int zeroLen = UNSIGNED5.getLength(DUMMY);
0398: archiveSize0 += UNSIGNED5.getLength(archiveSizeHi)
0399: - zeroLen;
0400: archiveSize0 += UNSIGNED5.getLength(archiveSizeLo)
0401: - zeroLen;
0402: }
0403: if (verbose > 1)
0404: Utils.log.fine("archive sizes: " + archiveSize0 + "+"
0405: + archiveSize1);
0406: assert (all_bands.outputSize() == archiveSize0 + archiveSize1);
0407: }
0408:
0409: void writeConstantPoolCounts(boolean haveNumbers)
0410: throws IOException {
0411: for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) {
0412: byte tag = ConstantPool.TAGS_IN_ORDER[k];
0413: int count = pkg.cp.getIndexByTag(tag).size();
0414: switch (tag) {
0415: case CONSTANT_Utf8:
0416: // The null string is always first.
0417: if (count > 0)
0418: assert (pkg.cp.getIndexByTag(tag).get(0) == ConstantPool
0419: .getUtf8Entry(""));
0420: break;
0421:
0422: case CONSTANT_Integer:
0423: case CONSTANT_Float:
0424: case CONSTANT_Long:
0425: case CONSTANT_Double:
0426: // Omit counts for numbers if possible.
0427: if (!haveNumbers) {
0428: assert (count == 0);
0429: continue;
0430: }
0431: break;
0432: }
0433: archive_header_1.putInt(count);
0434: }
0435: }
0436:
0437: protected Index getCPIndex(byte tag) {
0438: return pkg.cp.getIndexByTag(tag);
0439: }
0440:
0441: // (The following observations are out of date; they apply only to
0442: // "banding" the constant pool itself. Later revisions of this algorithm
0443: // applied the banding technique to every part of the package file,
0444: // applying the benefits more broadly.)
0445:
0446: // Note: Keeping the data separate in passes (or "bands") allows the
0447: // compressor to issue significantly shorter indexes for repeated data.
0448: // The difference in zipped size is 4%, which is remarkable since the
0449: // unzipped sizes are the same (only the byte order differs).
0450:
0451: // After moving similar data into bands, it becomes natural to delta-encode
0452: // each band. (This is especially useful if we sort the constant pool first.)
0453: // Delta encoding saves an extra 5% in the output size (13% of the CP itself).
0454: // Because a typical delta usees much less data than a byte, the savings after
0455: // zipping is even better: A zipped delta-encoded package is 8% smaller than
0456: // a zipped non-delta-encoded package. Thus, in the zipped file, a banded,
0457: // delta-encoded constant pool saves over 11% (of the total file size) compared
0458: // with a zipped unbanded file.
0459:
0460: void writeConstantPool() throws IOException {
0461: IndexGroup cp = pkg.cp;
0462:
0463: if (verbose > 0)
0464: Utils.log.info("Writing CP");
0465:
0466: for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) {
0467: byte tag = ConstantPool.TAGS_IN_ORDER[k];
0468: Index index = cp.getIndexByTag(tag);
0469:
0470: Entry[] cpMap = index.cpMap;
0471: if (verbose > 0)
0472: Utils.log.info("Writing " + cpMap.length + " "
0473: + ConstantPool.tagName(tag) + " entries...");
0474:
0475: if (optDumpBands) {
0476: PrintStream ps = new PrintStream(getDumpStream(index,
0477: ".idx"));
0478: printArrayTo(ps, cpMap, 0, cpMap.length);
0479: ps.close();
0480: }
0481:
0482: switch (tag) {
0483: case CONSTANT_Utf8:
0484: writeUtf8Bands(cpMap);
0485: break;
0486: case CONSTANT_Integer:
0487: for (int i = 0; i < cpMap.length; i++) {
0488: NumberEntry e = (NumberEntry) cpMap[i];
0489: int x = ((Integer) e.numberValue()).intValue();
0490: cp_Int.putInt(x);
0491: }
0492: break;
0493: case CONSTANT_Float:
0494: for (int i = 0; i < cpMap.length; i++) {
0495: NumberEntry e = (NumberEntry) cpMap[i];
0496: float fx = ((Float) e.numberValue()).floatValue();
0497: int x = Float.floatToIntBits(fx);
0498: cp_Float.putInt(x);
0499: }
0500: break;
0501: case CONSTANT_Long:
0502: for (int i = 0; i < cpMap.length; i++) {
0503: NumberEntry e = (NumberEntry) cpMap[i];
0504: long x = ((Long) e.numberValue()).longValue();
0505: cp_Long_hi.putInt((int) (x >>> 32));
0506: cp_Long_lo.putInt((int) (x >>> 0));
0507: }
0508: break;
0509: case CONSTANT_Double:
0510: for (int i = 0; i < cpMap.length; i++) {
0511: NumberEntry e = (NumberEntry) cpMap[i];
0512: double dx = ((Double) e.numberValue())
0513: .doubleValue();
0514: long x = Double.doubleToLongBits(dx);
0515: cp_Double_hi.putInt((int) (x >>> 32));
0516: cp_Double_lo.putInt((int) (x >>> 0));
0517: }
0518: break;
0519: case CONSTANT_String:
0520: for (int i = 0; i < cpMap.length; i++) {
0521: StringEntry e = (StringEntry) cpMap[i];
0522: cp_String.putRef(e.ref);
0523: }
0524: break;
0525: case CONSTANT_Class:
0526: for (int i = 0; i < cpMap.length; i++) {
0527: ClassEntry e = (ClassEntry) cpMap[i];
0528: cp_Class.putRef(e.ref);
0529: }
0530: break;
0531: case CONSTANT_Signature:
0532: writeSignatureBands(cpMap);
0533: break;
0534: case CONSTANT_NameandType:
0535: for (int i = 0; i < cpMap.length; i++) {
0536: DescriptorEntry e = (DescriptorEntry) cpMap[i];
0537: cp_Descr_name.putRef(e.nameRef);
0538: cp_Descr_type.putRef(e.typeRef);
0539: }
0540: break;
0541: case CONSTANT_Fieldref:
0542: writeMemberRefs(tag, cpMap, cp_Field_class,
0543: cp_Field_desc);
0544: break;
0545: case CONSTANT_Methodref:
0546: writeMemberRefs(tag, cpMap, cp_Method_class,
0547: cp_Method_desc);
0548: break;
0549: case CONSTANT_InterfaceMethodref:
0550: writeMemberRefs(tag, cpMap, cp_Imethod_class,
0551: cp_Imethod_desc);
0552: break;
0553: default:
0554: assert (false);
0555: }
0556: }
0557: }
0558:
0559: void writeUtf8Bands(Entry[] cpMap) throws IOException {
0560: if (cpMap.length == 0)
0561: return; // nothing to write
0562:
0563: // The first element must always be the empty string.
0564: assert (cpMap[0].stringValue().equals(""));
0565: final int SUFFIX_SKIP_1 = 1;
0566: final int PREFIX_SKIP_2 = 2;
0567:
0568: // Fetch the char arrays, first of all.
0569: char[][] chars = new char[cpMap.length][];
0570: for (int i = 0; i < chars.length; i++) {
0571: chars[i] = cpMap[i].stringValue().toCharArray();
0572: }
0573:
0574: // First band: Write lengths of shared prefixes.
0575: int[] prefixes = new int[cpMap.length]; // includes 2 skipped zeroes
0576: char[] prevChars = {};
0577: for (int i = 0; i < chars.length; i++) {
0578: int prefix = 0;
0579: char[] curChars = chars[i];
0580: int limit = Math.min(curChars.length, prevChars.length);
0581: while (prefix < limit
0582: && curChars[prefix] == prevChars[prefix])
0583: prefix++;
0584: prefixes[i] = prefix;
0585: if (i >= PREFIX_SKIP_2)
0586: cp_Utf8_prefix.putInt(prefix);
0587: else
0588: assert (prefix == 0);
0589: prevChars = curChars;
0590: }
0591:
0592: // Second band: Write lengths of unshared suffixes.
0593: // Third band: Write the char values in the unshared suffixes.
0594: for (int i = 0; i < chars.length; i++) {
0595: char[] str = chars[i];
0596: int prefix = prefixes[i];
0597: int suffix = str.length - prefixes[i];
0598: boolean isPacked = false;
0599: if (suffix == 0) {
0600: // Zero suffix length is special flag to indicate
0601: // separate treatment in cp_Utf8_big bands.
0602: // This suffix length never occurs naturally,
0603: // except in the one case of a zero-length string.
0604: // (If it occurs, it is the first, due to sorting.)
0605: // The zero length string must, paradoxically, be
0606: // encoded as a zero-length cp_Utf8_big band.
0607: // This wastes exactly (& tolerably) one null byte.
0608: isPacked = (i >= SUFFIX_SKIP_1);
0609: // Do not bother to add an empty "(Utf8_big_0)" band.
0610: // Also, the initial empty string does not require a band.
0611: } else if (optBigStrings && effort > 1 && suffix > 100) {
0612: int numWide = 0;
0613: for (int n = 0; n < suffix; n++) {
0614: if (str[prefix + n] > 127) {
0615: numWide++;
0616: }
0617: }
0618: if (numWide > 100) {
0619: // Try packing the chars with an alternate encoding.
0620: isPacked = tryAlternateEncoding(i, numWide, str,
0621: prefix);
0622: }
0623: }
0624: if (i < SUFFIX_SKIP_1) {
0625: // No output.
0626: assert (!isPacked);
0627: assert (suffix == 0);
0628: } else if (isPacked) {
0629: // Mark packed string with zero-length suffix count.
0630: // This tells the unpacker to go elsewhere for the suffix bits.
0631: // Fourth band: Write unshared suffix with alternate coding.
0632: cp_Utf8_suffix.putInt(0);
0633: cp_Utf8_big_suffix.putInt(suffix);
0634: } else {
0635: assert (suffix != 0); // would be ambiguous
0636: // Normal string. Save suffix in third and fourth bands.
0637: cp_Utf8_suffix.putInt(suffix);
0638: for (int n = 0; n < suffix; n++) {
0639: int ch = str[prefix + n];
0640: cp_Utf8_chars.putInt(ch);
0641: }
0642: }
0643: }
0644: if (verbose > 0) {
0645: int normCharCount = cp_Utf8_chars.length();
0646: int packCharCount = cp_Utf8_big_chars.length();
0647: int charCount = normCharCount + packCharCount;
0648: Utils.log.info("Utf8string #CHARS=" + charCount
0649: + " #PACKEDCHARS=" + packCharCount);
0650: }
0651: }
0652:
0653: private boolean tryAlternateEncoding(int i, int numWide,
0654: char[] str, int prefix) {
0655: int suffix = str.length - prefix;
0656: int[] cvals = new int[suffix];
0657: for (int n = 0; n < suffix; n++) {
0658: cvals[n] = str[prefix + n];
0659: }
0660: CodingChooser cc = getCodingChooser();
0661: Coding bigRegular = cp_Utf8_big_chars.regularCoding;
0662: String bandName = "(Utf8_big_" + i + ")";
0663: int[] sizes = { 0, 0 };
0664: final int BYTE_SIZE = CodingChooser.BYTE_SIZE;
0665: final int ZIP_SIZE = CodingChooser.ZIP_SIZE;
0666: if (verbose > 1 || cc.verbose > 1) {
0667: Utils.log.fine("--- chooseCoding " + bandName);
0668: }
0669: CodingMethod special = cc.choose(cvals, bigRegular, sizes);
0670: Coding charRegular = cp_Utf8_chars.regularCoding;
0671: if (verbose > 1)
0672: Utils.log.fine("big string[" + i + "] len=" + suffix
0673: + " #wide=" + numWide + " size=" + sizes[BYTE_SIZE]
0674: + "/z=" + sizes[ZIP_SIZE] + " coding " + special);
0675: if (special != charRegular) {
0676: int specialZipSize = sizes[ZIP_SIZE];
0677: int[] normalSizes = cc.computeSize(charRegular, cvals);
0678: int normalZipSize = normalSizes[ZIP_SIZE];
0679: int minWin = Math.max(5, normalZipSize / 1000);
0680: if (verbose > 1)
0681: Utils.log.fine("big string[" + i + "] normalSize="
0682: + normalSizes[BYTE_SIZE] + "/z="
0683: + normalSizes[ZIP_SIZE] + " win="
0684: + (specialZipSize < normalZipSize - minWin));
0685: if (specialZipSize < normalZipSize - minWin) {
0686: IntBand big = cp_Utf8_big_chars.newIntBand(bandName);
0687: big.initializeValues(cvals);
0688: return true;
0689: }
0690: }
0691: return false;
0692: }
0693:
0694: void writeSignatureBands(Entry[] cpMap) throws IOException {
0695: for (int i = 0; i < cpMap.length; i++) {
0696: SignatureEntry e = (SignatureEntry) cpMap[i];
0697: cp_Signature_form.putRef(e.formRef);
0698: for (int j = 0; j < e.classRefs.length; j++) {
0699: cp_Signature_classes.putRef(e.classRefs[j]);
0700: }
0701: }
0702: }
0703:
0704: void writeMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class,
0705: CPRefBand cp_desc) throws IOException {
0706: for (int i = 0; i < cpMap.length; i++) {
0707: MemberEntry e = (MemberEntry) cpMap[i];
0708: cp_class.putRef(e.classRef);
0709: cp_desc.putRef(e.descRef);
0710: }
0711: }
0712:
0713: void writeFiles() throws IOException {
0714: int numFiles = pkg.files.size();
0715: if (numFiles == 0)
0716: return;
0717: int options = archiveOptions;
0718: boolean haveSizeHi = testBit(options, AO_HAVE_FILE_SIZE_HI);
0719: boolean haveModtime = testBit(options, AO_HAVE_FILE_MODTIME);
0720: boolean haveOptions = testBit(options, AO_HAVE_FILE_OPTIONS);
0721: if (!haveOptions) {
0722: for (Iterator i = pkg.files.iterator(); i.hasNext();) {
0723: File file = (File) i.next();
0724: if (file.isClassStub()) {
0725: haveOptions = true;
0726: options |= AO_HAVE_FILE_OPTIONS;
0727: archiveOptions = options;
0728: break;
0729: }
0730: }
0731: }
0732: if (haveSizeHi || haveModtime || haveOptions
0733: || !pkg.files.isEmpty()) {
0734: options |= AO_HAVE_FILE_HEADERS;
0735: archiveOptions = options;
0736: }
0737:
0738: for (Iterator i = pkg.files.iterator(); i.hasNext();) {
0739: File file = (File) i.next();
0740: file_name.putRef(file.name);
0741: long len = file.getFileLength();
0742: file_size_lo.putInt((int) len);
0743: if (haveSizeHi)
0744: file_size_hi.putInt((int) (len >>> 32));
0745: if (haveModtime)
0746: file_modtime.putInt(file.modtime - pkg.default_modtime);
0747: if (haveOptions)
0748: file_options.putInt(file.options);
0749: file.writeTo(file_bits.collectorStream());
0750: if (verbose > 1)
0751: Utils.log.fine("Wrote " + len + " bytes of "
0752: + file.name.stringValue());
0753: }
0754: if (verbose > 0)
0755: Utils.log.info("Wrote " + numFiles + " resource files");
0756: }
0757:
0758: void collectAttributeLayouts() {
0759: maxFlags = new int[ATTR_CONTEXT_LIMIT];
0760: allLayouts = new HashMap[ATTR_CONTEXT_LIMIT];
0761: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0762: allLayouts[i] = new HashMap();
0763: }
0764: // Collect maxFlags and allLayouts.
0765: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
0766: Class cls = (Class) i.next();
0767: visitAttributeLayoutsIn(ATTR_CONTEXT_CLASS, cls);
0768: for (Iterator j = cls.getFields().iterator(); j.hasNext();) {
0769: Class.Field f = (Class.Field) j.next();
0770: visitAttributeLayoutsIn(ATTR_CONTEXT_FIELD, f);
0771: }
0772: for (Iterator j = cls.getMethods().iterator(); j.hasNext();) {
0773: Class.Method m = (Class.Method) j.next();
0774: visitAttributeLayoutsIn(ATTR_CONTEXT_METHOD, m);
0775: if (m.code != null) {
0776: visitAttributeLayoutsIn(ATTR_CONTEXT_CODE, m.code);
0777: }
0778: }
0779: }
0780: // If there are many species of attributes, use 63-bit flags.
0781: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0782: int nl = allLayouts[i].size();
0783: boolean haveLongFlags = haveFlagsHi(i);
0784: final int TOO_MANY_ATTRS = 32 /*int flag size*/
0785: - 12 /*typical flag bits in use*/
0786: + 4 /*typical number of OK overflows*/;
0787: if (nl >= TOO_MANY_ATTRS) { // heuristic
0788: int mask = 1 << (LG_AO_HAVE_XXX_FLAGS_HI + i);
0789: archiveOptions |= mask;
0790: haveLongFlags = true;
0791: if (verbose > 0)
0792: Utils.log.info("Note: Many "
0793: + Attribute.contextName(i)
0794: + " attributes forces 63-bit flags");
0795: }
0796: if (verbose > 1) {
0797: Utils.log.fine(Attribute.contextName(i)
0798: + ".maxFlags = 0x"
0799: + Integer.toHexString(maxFlags[i]));
0800: Utils.log.fine(Attribute.contextName(i)
0801: + ".#layouts = " + nl);
0802: }
0803: assert (haveFlagsHi(i) == haveLongFlags);
0804: }
0805: initAttrIndexLimit();
0806:
0807: // Standard indexes can never conflict with flag bits. Assert it.
0808: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0809: assert ((attrFlagMask[i] & maxFlags[i]) == 0);
0810: }
0811: // Collect counts for both predefs. and custom defs.
0812: // Decide on custom, local attribute definitions.
0813: backCountTable = new HashMap();
0814: attrCounts = new int[ATTR_CONTEXT_LIMIT][];
0815: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0816: // Now the remaining defs in allLayouts[i] need attr. indexes.
0817: // Fill up unused flag bits with new defs.
0818: // Unused bits are those which are not used by predefined attrs,
0819: // and which are always clear in the classfiles.
0820: long avHiBits = ~(maxFlags[i] | attrFlagMask[i]);
0821: assert (attrIndexLimit[i] > 0);
0822: assert (attrIndexLimit[i] < 64); // all bits fit into a Java long
0823: avHiBits &= (1L << attrIndexLimit[i]) - 1;
0824: int nextLoBit = 0;
0825: Map.Entry[] layoutsAndCounts = new Map.Entry[allLayouts[i]
0826: .size()];
0827: allLayouts[i].entrySet().toArray(layoutsAndCounts);
0828: // Sort by count, most frequent first.
0829: // Predefs. participate in this sort, though it does not matter.
0830: Arrays.sort(layoutsAndCounts, new Comparator() {
0831: public int compare(Object o0, Object o1) {
0832: Map.Entry e0 = (Map.Entry) o0;
0833: Map.Entry e1 = (Map.Entry) o1;
0834: // Primary sort key is count, reversed.
0835: int r = -(((int[]) e0.getValue())[0] - ((int[]) e1
0836: .getValue())[0]);
0837: if (r != 0)
0838: return r;
0839: return ((Comparable) e0.getKey()).compareTo(e1
0840: .getKey());
0841: }
0842: });
0843: attrCounts[i] = new int[attrIndexLimit[i]
0844: + layoutsAndCounts.length];
0845: for (int j = 0; j < layoutsAndCounts.length; j++) {
0846: Map.Entry e = layoutsAndCounts[j];
0847: Attribute.Layout def = (Attribute.Layout) e.getKey();
0848: int count = ((int[]) e.getValue())[0];
0849: int index;
0850: Integer predefIndex = (Integer) attrIndexTable.get(def);
0851: if (predefIndex != null) {
0852: // The index is already set.
0853: index = predefIndex.intValue();
0854: } else if (avHiBits != 0) {
0855: while ((avHiBits & 1) == 0) {
0856: avHiBits >>>= 1;
0857: nextLoBit += 1;
0858: }
0859: avHiBits -= 1; // clear low bit; we are using it now
0860: // Update attrIndexTable:
0861: index = setAttributeLayoutIndex(def, nextLoBit);
0862: } else {
0863: // Update attrIndexTable:
0864: index = setAttributeLayoutIndex(def,
0865: ATTR_INDEX_OVERFLOW);
0866: }
0867:
0868: // Now that we know the index, record the count of this def.
0869: attrCounts[i][index] = count;
0870:
0871: // For all callables in the def, keep a tally of back-calls.
0872: Attribute.Layout.Element[] cbles = def.getCallables();
0873: final int[] bc = new int[cbles.length];
0874: for (int k = 0; k < cbles.length; k++) {
0875: assert (cbles[k].kind == Attribute.EK_CBLE);
0876: if (!cbles[k].flagTest(Attribute.EF_BACK)) {
0877: bc[k] = -1; // no count to accumulate here
0878: }
0879: }
0880: backCountTable.put(def, bc);
0881:
0882: if (predefIndex == null) {
0883: // Make sure the package CP can name the local attribute.
0884: Entry ne = ConstantPool.getUtf8Entry(def.name());
0885: String layout = def
0886: .layoutForPackageMajver(getPackageMajver());
0887: Entry le = ConstantPool.getUtf8Entry(layout);
0888: requiredEntries.add(ne);
0889: requiredEntries.add(le);
0890: if (verbose > 0) {
0891: if (index < attrIndexLimit[i])
0892: Utils.log.info("Using free flag bit 1<<"
0893: + index + " for " + count
0894: + " occurrences of " + def);
0895: else
0896: Utils.log.info("Using overflow index "
0897: + index + " for " + count
0898: + " occurrences of " + def);
0899: }
0900: }
0901: }
0902: }
0903: // Later, when emitting attr_definition_bands, we will look at
0904: // attrDefSeen and attrDefs at position 32/63 and beyond.
0905: // The attrIndexTable will provide elements of xxx_attr_indexes bands.
0906:
0907: // Done with scratch variables:
0908: maxFlags = null;
0909: allLayouts = null;
0910: }
0911:
0912: // Scratch variables for processing attributes and flags.
0913: int[] maxFlags;
0914: HashMap[] allLayouts;
0915:
0916: void visitAttributeLayoutsIn(int ctype, Attribute.Holder h) {
0917: // Make note of which flags appear in the class file.
0918: // Set them in maxFlags.
0919: maxFlags[ctype] |= h.flags;
0920: for (Iterator i = h.getAttributes().iterator(); i.hasNext();) {
0921: Attribute a = (Attribute) i.next();
0922: Attribute.Layout def = a.layout();
0923: int[] count = (int[]) allLayouts[ctype].get(def);
0924: if (count == null)
0925: allLayouts[ctype].put(def, count = new int[1]);
0926: if (count[0] < Integer.MAX_VALUE)
0927: count[0] += 1;
0928: }
0929: }
0930:
0931: Attribute.Layout[] attrDefsWritten;
0932:
0933: void writeAttrDefs() throws IOException {
0934: ArrayList defList = new ArrayList();
0935: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0936: int limit = attrDefs[i].size();
0937: for (int j = 0; j < limit; j++) {
0938: int header = i; // ctype
0939: if (j < attrIndexLimit[i]) {
0940: header |= ((j + ADH_BIT_IS_LSB) << ADH_BIT_SHIFT);
0941: assert (header < 0x100); // must fit into a byte
0942: // (...else header is simply ctype, with zero high bits.)
0943: if (!testBit(attrDefSeen[i], 1L << j)) {
0944: // either undefined or predefined; nothing to write
0945: continue;
0946: }
0947: }
0948: Attribute.Layout def = (Attribute.Layout) attrDefs[i]
0949: .get(j);
0950: defList.add(new Object[] { new Integer(header), def });
0951: assert (new Integer(j).equals(attrIndexTable.get(def)));
0952: }
0953: }
0954: // Sort the new attr defs into some "natural" order.
0955: int numAttrDefs = defList.size();
0956: Object[][] defs = new Object[numAttrDefs][];
0957: defList.toArray(defs);
0958: Arrays.sort(defs, new Comparator() {
0959: public int compare(Object o0, Object o1) {
0960: Object[] a0 = (Object[]) o0;
0961: Object[] a1 = (Object[]) o1;
0962: // Primary sort key is attr def header.
0963: int r = ((Comparable) a0[0]).compareTo(a1[0]);
0964: if (r != 0)
0965: return r;
0966: Object ind0 = attrIndexTable.get(a0[1]);
0967: Object ind1 = attrIndexTable.get(a1[1]);
0968: // Secondary sort key is attribute index.
0969: // (This must be so, in order to keep overflow attr order.)
0970: assert (ind0 != null);
0971: assert (ind1 != null);
0972: return ((Comparable) ind0).compareTo(ind1);
0973: }
0974: });
0975: attrDefsWritten = new Attribute.Layout[numAttrDefs];
0976: PrintStream dump = !optDumpBands ? null : new PrintStream(
0977: getDumpStream(attr_definition_headers, ".def"));
0978: int[] indexForDebug = new int[ATTR_CONTEXT_LIMIT];
0979: for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
0980: indexForDebug[i] = attrIndexLimit[i];
0981: }
0982: for (int i = 0; i < defs.length; i++) {
0983: int header = ((Integer) defs[i][0]).intValue();
0984: Attribute.Layout def = (Attribute.Layout) defs[i][1];
0985: attrDefsWritten[i] = def;
0986: assert ((header & ADH_CONTEXT_MASK) == def.ctype());
0987: attr_definition_headers.putByte(header);
0988: attr_definition_name.putRef(ConstantPool.getUtf8Entry(def
0989: .name()));
0990: String layout = def
0991: .layoutForPackageMajver(getPackageMajver());
0992: attr_definition_layout.putRef(ConstantPool
0993: .getUtf8Entry(layout));
0994: // Check that we are transmitting that correct attribute index:
0995: boolean debug = false;
0996: assert (debug = true);
0997: if (debug) {
0998: int hdrIndex = (header >> ADH_BIT_SHIFT)
0999: - ADH_BIT_IS_LSB;
1000: if (hdrIndex < 0)
1001: hdrIndex = indexForDebug[def.ctype()]++;
1002: int realIndex = ((Integer) attrIndexTable.get(def))
1003: .intValue();
1004: assert (hdrIndex == realIndex);
1005: }
1006: if (dump != null) {
1007: int index = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB;
1008: dump.println(index + " " + def);
1009: }
1010: }
1011: if (dump != null)
1012: dump.close();
1013: }
1014:
1015: void writeAttrCounts() throws IOException {
1016: // Write the four xxx_attr_calls bands.
1017: for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) {
1018: MultiBand xxx_attr_bands = attrBands[ctype];
1019: IntBand xxx_attr_calls = getAttrBand(xxx_attr_bands,
1020: AB_ATTR_CALLS);
1021: Attribute.Layout[] defs = new Attribute.Layout[attrDefs[ctype]
1022: .size()];
1023: attrDefs[ctype].toArray(defs);
1024: for (boolean predef = true;; predef = false) {
1025: for (int ai = 0; ai < defs.length; ai++) {
1026: Attribute.Layout def = defs[ai];
1027: if (def == null)
1028: continue; // unused index
1029: if (predef != isPredefinedAttr(ctype, ai))
1030: continue; // wrong pass
1031: int totalCount = attrCounts[ctype][ai];
1032: if (totalCount == 0)
1033: continue; // irrelevant
1034: int[] bc = (int[]) backCountTable.get(def);
1035: for (int j = 0; j < bc.length; j++) {
1036: if (bc[j] >= 0) {
1037: int backCount = bc[j];
1038: bc[j] = -1; // close out; do not collect further counts
1039: xxx_attr_calls.putInt(backCount);
1040: assert (def.getCallables()[j]
1041: .flagTest(Attribute.EF_BACK));
1042: } else {
1043: assert (!def.getCallables()[j]
1044: .flagTest(Attribute.EF_BACK));
1045: }
1046: }
1047: }
1048: if (!predef)
1049: break;
1050: }
1051: }
1052: }
1053:
1054: void trimClassAttributes() {
1055: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
1056: Class cls = (Class) i.next();
1057: // Replace "obvious" SourceFile attrs by null.
1058: cls.minimizeSourceFile();
1059: }
1060: }
1061:
1062: void collectInnerClasses() {
1063: // Capture inner classes, removing them from individual classes.
1064: // Irregular inner classes must stay local, though.
1065: HashMap allICMap = new HashMap();
1066: // First, collect a consistent global set.
1067: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
1068: Class cls = (Class) i.next();
1069: if (!cls.hasInnerClasses())
1070: continue;
1071: for (Iterator j = cls.getInnerClasses().iterator(); j
1072: .hasNext();) {
1073: InnerClass ic = (InnerClass) j.next();
1074: InnerClass pic = (InnerClass) allICMap.put(
1075: ic.this Class, ic);
1076: if (pic != null && !pic.equals(ic) && pic.predictable) {
1077: // Different ICs. Choose the better to make global.
1078: allICMap.put(pic.this Class, pic);
1079: }
1080: }
1081: }
1082:
1083: InnerClass[] allICs = new InnerClass[allICMap.size()];
1084: allICMap.values().toArray(allICs);
1085: allICMap = null; // done with it
1086:
1087: // Note: The InnerClasses attribute must be in a valid order,
1088: // so that A$B always occurs earlier than A$B$C. This is an
1089: // important side-effect of sorting lexically by class name.
1090: Arrays.sort(allICs); // put in canonical order
1091: pkg.setAllInnerClasses(Arrays.asList(allICs));
1092:
1093: // Next, empty out of every local set the consistent entries.
1094: // Calculate whether there is any remaining need to have a local
1095: // set, and whether it needs to be locked.
1096: for (Iterator i = pkg.classes.iterator(); i.hasNext();) {
1097: Class cls = (Class) i.next();
1098: cls.minimizeLocalICs();
1099: }
1100: }
1101:
1102: void writeInnerClasses() throws IOException {
1103: for (Iterator i = pkg.getAllInnerClasses().iterator(); i
1104: .hasNext();) {
1105: InnerClass ic = (InnerClass) i.next();
1106: int flags = ic.flags;
1107: assert ((flags & ACC_IC_LONG_FORM) == 0);
1108: if (!ic.predictable) {
1109: flags |= ACC_IC_LONG_FORM;
1110: }
1111: ic_this _class.putRef(ic.this Class);
1112: ic_flags.putInt(flags);
1113: if (!ic.predictable) {
1114: ic_outer_class.putRef(ic.outerClass);
1115: ic_name.putRef(ic.name);
1116: }
1117: }
1118: }
1119:
1120: /** If there are any extra InnerClasses entries to write which are
1121: * not already implied by the global table, put them into a
1122: * local attribute. This is expected to be rare.
1123: */
1124: void writeLocalInnerClasses(Class cls) throws IOException {
1125: List localICs = cls.getInnerClasses();
1126: class_InnerClasses_N.putInt(localICs.size());
1127: for (Iterator i = localICs.iterator(); i.hasNext();) {
1128: InnerClass ic = (InnerClass) i.next();
1129: class_InnerClasses_RC.putRef(ic.this Class);
1130: // Is it redundant with the global version?
1131: if (ic.equals(pkg.getGlobalInnerClass(ic.this Class))) {
1132: // A zero flag means copy a global IC here.
1133: class_InnerClasses_F.putInt(0);
1134: } else {
1135: int flags = ic.flags;
1136: if (flags == 0)
1137: flags = ACC_IC_LONG_FORM; // force it to be non-zero
1138: class_InnerClasses_F.putInt(flags);
1139: class_InnerClasses_outer_RCN.putRef(ic.outerClass);
1140: class_InnerClasses_name_RUN.putRef(ic.name);
1141: }
1142: }
1143: }
1144:
1145: void writeClassesAndByteCodes() throws IOException {
1146: Class[] classes = new Class[pkg.classes.size()];
1147: pkg.classes.toArray(classes);
1148: // Note: This code respects the order in which caller put classes.
1149: if (verbose > 0)
1150: Utils.log.info(" ...scanning " + classes.length
1151: + " classes...");
1152:
1153: int nwritten = 0;
1154: for (int i = 0; i < classes.length; i++) {
1155: // Collect the class body, sans bytecodes.
1156: Class cls = classes[i];
1157: if (verbose > 1)
1158: Utils.log.fine("Scanning " + cls);
1159:
1160: ClassEntry this Class = cls.this Class;
1161: ClassEntry super Class = cls.super Class;
1162: ClassEntry[] interfaces = cls.interfaces;
1163: // Encode rare case of null superClass as thisClass:
1164: assert (super Class != this Class); // bad class file!?
1165: if (super Class == null)
1166: super Class = this Class;
1167: class_this .putRef(this Class);
1168: class_super .putRef(super Class);
1169: class_interface_count.putInt(cls.interfaces.length);
1170: for (int j = 0; j < interfaces.length; j++) {
1171: class_interface.putRef(interfaces[j]);
1172: }
1173:
1174: writeMembers(cls);
1175: writeAttrs(ATTR_CONTEXT_CLASS, cls, cls);
1176:
1177: nwritten++;
1178: if (verbose > 0 && (nwritten % 1000) == 0)
1179: Utils.log.info("Have scanned " + nwritten
1180: + " classes...");
1181: }
1182: }
1183:
1184: void writeMembers(Class cls) throws IOException {
1185: List fields = cls.getFields();
1186: class_field_count.putInt(fields.size());
1187: for (Iterator i = fields.iterator(); i.hasNext();) {
1188: Class.Field f = (Class.Field) i.next();
1189: field_descr.putRef(f.getDescriptor());
1190: writeAttrs(ATTR_CONTEXT_FIELD, f, cls);
1191: }
1192:
1193: List methods = cls.getMethods();
1194: class_method_count.putInt(methods.size());
1195: for (Iterator i = methods.iterator(); i.hasNext();) {
1196: Class.Method m = (Class.Method) i.next();
1197: method_descr.putRef(m.getDescriptor());
1198: writeAttrs(ATTR_CONTEXT_METHOD, m, cls);
1199: assert ((m.code != null) == (m.getAttribute(attrCodeEmpty) != null));
1200: if (m.code != null) {
1201: writeCodeHeader(m.code);
1202: writeByteCodes(m.code);
1203: }
1204: }
1205: }
1206:
1207: void writeCodeHeader(Code c) throws IOException {
1208: boolean attrsOK = testBit(archiveOptions,
1209: AO_HAVE_ALL_CODE_FLAGS);
1210: int na = c.attributeSize();
1211: int sc = shortCodeHeader(c);
1212: if (!attrsOK && na > 0)
1213: // We must write flags, and can only do so for long headers.
1214: sc = LONG_CODE_HEADER;
1215: if (verbose > 2) {
1216: int siglen = c.getMethod().getArgumentSize();
1217: Utils.log.fine("Code sizes info " + c.max_stack + " "
1218: + c.max_locals + " " + c.getHandlerCount() + " "
1219: + siglen + " " + na
1220: + (sc > 0 ? " SHORT=" + sc : ""));
1221: }
1222: code_headers.putByte(sc);
1223: if (sc == LONG_CODE_HEADER) {
1224: code_max_stack.putInt(c.getMaxStack());
1225: code_max_na_locals.putInt(c.getMaxNALocals());
1226: code_handler_count.putInt(c.getHandlerCount());
1227: } else {
1228: assert (attrsOK || na == 0);
1229: assert (c.getHandlerCount() < shortCodeHeader_h_limit);
1230: }
1231: writeCodeHandlers(c);
1232: if (sc == LONG_CODE_HEADER || attrsOK)
1233: writeAttrs(ATTR_CONTEXT_CODE, c, c.this Class());
1234: }
1235:
1236: void writeCodeHandlers(Code c) throws IOException {
1237: int sum, del;
1238: for (int j = 0, jmax = c.getHandlerCount(); j < jmax; j++) {
1239: code_handler_class_RCN.putRef(c.handler_class[j]); // null OK
1240: // Encode end as offset from start, and catch as offset from end,
1241: // because they are strongly correlated.
1242: sum = c.encodeBCI(c.handler_start[j]);
1243: code_handler_start_P.putInt(sum);
1244: del = c.encodeBCI(c.handler_end[j]) - sum;
1245: code_handler_end_PO.putInt(del);
1246: sum += del;
1247: del = c.encodeBCI(c.handler_catch[j]) - sum;
1248: code_handler_catch_PO.putInt(del);
1249: }
1250: }
1251:
1252: // Generic routines for writing attributes and flags of
1253: // classes, fields, methods, and codes.
1254: void writeAttrs(int ctype, final Attribute.Holder h, Class cls)
1255: throws IOException {
1256: MultiBand xxx_attr_bands = attrBands[ctype];
1257: IntBand xxx_flags_hi = getAttrBand(xxx_attr_bands, AB_FLAGS_HI);
1258: IntBand xxx_flags_lo = getAttrBand(xxx_attr_bands, AB_FLAGS_LO);
1259: boolean haveLongFlags = haveFlagsHi(ctype);
1260: assert (attrIndexLimit[ctype] == (haveLongFlags ? 63 : 32));
1261: if (h.attributes == null) {
1262: xxx_flags_lo.putInt(h.flags); // no extra bits to set here
1263: if (haveLongFlags)
1264: xxx_flags_hi.putInt(0);
1265: return;
1266: }
1267: if (verbose > 3)
1268: Utils.log.fine("Transmitting attrs for " + h + " flags="
1269: + Integer.toHexString(h.flags));
1270:
1271: long flagMask = attrFlagMask[ctype]; // which flags are attr bits?
1272: long flagsToAdd = 0;
1273: int overflowCount = 0;
1274: for (ListIterator j = h.attributes.listIterator(); j.hasNext();) {
1275: Attribute a = (Attribute) j.next();
1276: Attribute.Layout def = a.layout();
1277: int index = ((Integer) attrIndexTable.get(def)).intValue();
1278: assert (attrDefs[ctype].get(index) == def);
1279: if (verbose > 3)
1280: Utils.log.fine("add attr @" + index + " " + a + " in "
1281: + h);
1282: if (index < attrIndexLimit[ctype]
1283: && testBit(flagMask, 1L << index)) {
1284: if (verbose > 3)
1285: Utils.log.fine("Adding flag bit 1<<" + index
1286: + " in " + Long.toHexString(flagMask));
1287: assert (!testBit(h.flags, 1L << index));
1288: flagsToAdd |= (1L << index);
1289: flagMask -= (1L << index); // do not use this bit twice here
1290: } else {
1291: // an overflow attr.
1292: flagsToAdd |= (1L << X_ATTR_OVERFLOW);
1293: overflowCount += 1;
1294: if (verbose > 3)
1295: Utils.log.fine("Adding overflow attr #"
1296: + overflowCount);
1297: IntBand xxx_attr_indexes = getAttrBand(xxx_attr_bands,
1298: AB_ATTR_INDEXES);
1299: xxx_attr_indexes.putInt(index);
1300: // System.out.println("overflow @"+index);
1301: }
1302: if (def.bandCount == 0) {
1303: if (def == attrInnerClassesEmpty) {
1304: // Special logic to write this attr.
1305: writeLocalInnerClasses((Class) h);
1306: continue;
1307: }
1308: // Empty attr; nothing more to write here.
1309: continue;
1310: }
1311: assert (a.fixups == null);
1312: final Band[] ab = (Band[]) attrBandTable.get(def);
1313: assert (ab != null);
1314: assert (ab.length == def.bandCount);
1315: final int[] bc = (int[]) backCountTable.get(def);
1316: assert (bc != null);
1317: assert (bc.length == def.getCallables().length);
1318: // Write one attribute of type def into ab.
1319: if (verbose > 2)
1320: Utils.log.fine("writing " + a + " in " + h);
1321: boolean isCV = (ctype == ATTR_CONTEXT_FIELD && def == attrConstantValue);
1322: if (isCV)
1323: setConstantValueIndex((Class.Field) h);
1324: a.parse(cls, a.bytes(), 0, a.size(),
1325: new Attribute.ValueStream() {
1326: public void putInt(int bandIndex, int value) {
1327: ((IntBand) ab[bandIndex]).putInt(value);
1328: }
1329:
1330: public void putRef(int bandIndex, Entry ref) {
1331: ((CPRefBand) ab[bandIndex]).putRef(ref);
1332: }
1333:
1334: public int encodeBCI(int bci) {
1335: Code code = (Code) h;
1336: return code.encodeBCI(bci);
1337: }
1338:
1339: public void noteBackCall(int whichCallable) {
1340: assert (bc[whichCallable] >= 0);
1341: bc[whichCallable] += 1;
1342: }
1343: });
1344: if (isCV)
1345: setConstantValueIndex(null); // clean up
1346: }
1347:
1348: if (overflowCount > 0) {
1349: IntBand xxx_attr_count = getAttrBand(xxx_attr_bands,
1350: AB_ATTR_COUNT);
1351: xxx_attr_count.putInt(overflowCount);
1352: }
1353:
1354: xxx_flags_lo.putInt(h.flags | (int) flagsToAdd);
1355: if (haveLongFlags)
1356: xxx_flags_hi.putInt((int) (flagsToAdd >>> 32));
1357: else
1358: assert ((flagsToAdd >>> 32) == 0);
1359: assert ((h.flags & flagsToAdd) == 0) : (h + ".flags="
1360: + Integer.toHexString(h.flags) + "^" + Long
1361: .toHexString(flagsToAdd));
1362: }
1363:
1364: // temporary scratch variables for processing code blocks
1365: private Code curCode;
1366: private Class curClass;
1367: private Entry[] curCPMap;
1368:
1369: private void beginCode(Code c) {
1370: assert (curCode == null);
1371: curCode = c;
1372: curClass = c.m.this Class();
1373: curCPMap = c.getCPMap();
1374: }
1375:
1376: private void endCode() {
1377: curCode = null;
1378: curClass = null;
1379: curCPMap = null;
1380: }
1381:
1382: // Return an _invokeinit_op variant, if the instruction matches one,
1383: // else -1.
1384: private int initOpVariant(Instruction i, Entry newClass) {
1385: if (i.getBC() != _invokespecial)
1386: return -1;
1387: MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap);
1388: if (ref.descRef.nameRef.stringValue() != "<init>")
1389: return -1;
1390: ClassEntry refClass = ref.classRef;
1391: if (refClass == curClass.this Class)
1392: return _invokeinit_op + _invokeinit_self_option;
1393: if (refClass == curClass.super Class)
1394: return _invokeinit_op + _invokeinit_super _option;
1395: if (refClass == newClass)
1396: return _invokeinit_op + _invokeinit_new_option;
1397: return -1;
1398: }
1399:
1400: // Return a _self_linker_op variant, if the instruction matches one,
1401: // else -1.
1402: private int selfOpVariant(Instruction i) {
1403: int bc = i.getBC();
1404: if (!(bc >= _first_linker_op && bc <= _last_linker_op))
1405: return -1;
1406: MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap);
1407: ClassEntry refClass = ref.classRef;
1408: int self_bc = _self_linker_op + (bc - _first_linker_op);
1409: if (refClass == curClass.this Class)
1410: return self_bc;
1411: if (refClass == curClass.super Class)
1412: return self_bc + _self_linker_super _flag;
1413: return -1;
1414: }
1415:
1416: void writeByteCodes(Code code) throws IOException {
1417: beginCode(code);
1418: IndexGroup cp = pkg.cp;
1419:
1420: // true if the previous instruction is an aload to absorb
1421: boolean prevAload = false;
1422:
1423: // class of most recent new; helps compress <init> calls
1424: Entry newClass = null;
1425:
1426: for (Instruction i = code.instructionAt(0); i != null; i = i
1427: .next()) {
1428: // %%% Add a stress mode which issues _ref/_byte_escape.
1429: if (verbose > 3)
1430: Utils.log.fine(i.toString());
1431:
1432: if (i.isNonstandard()
1433: && (!p200.getBoolean(Utils.COM_PREFIX
1434: + "invokedynamic") || i.getBC() != _xxxunusedxxx)) {
1435: // Crash and burn with a complaint if there are funny
1436: // bytecodes in this class file.
1437: String complaint = code.getMethod()
1438: + " contains an unrecognized bytecode "
1439: + i
1440: + "; please use the pass-file option on this class.";
1441: Utils.log.warning(complaint);
1442: throw new IOException(complaint);
1443: }
1444:
1445: if (i.isWide()) {
1446: if (verbose > 1) {
1447: Utils.log.fine("_wide opcode in " + code);
1448: Utils.log.fine(i.toString());
1449: }
1450: bc_codes.putByte(_wide);
1451: codeHist[_wide]++;
1452: }
1453:
1454: int bc = i.getBC();
1455:
1456: // Begin "bc_linker" compression.
1457: if (bc == _aload_0) {
1458: // Try to group aload_0 with a following operation.
1459: Instruction ni = code.instructionAt(i.getNextPC());
1460: if (selfOpVariant(ni) >= 0) {
1461: prevAload = true;
1462: continue;
1463: }
1464: }
1465:
1466: // Test for <init> invocations:
1467: int init_bc = initOpVariant(i, newClass);
1468: if (init_bc >= 0) {
1469: if (prevAload) {
1470: // get rid of it
1471: bc_codes.putByte(_aload_0);
1472: codeHist[_aload_0]++;
1473: prevAload = false; //used up
1474: }
1475: // Write special bytecode.
1476: bc_codes.putByte(init_bc);
1477: codeHist[init_bc]++;
1478: MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap);
1479: // Write operand to a separate band.
1480: int coding = cp.getOverloadingIndex(ref);
1481: bc_initref.putInt(coding);
1482: continue;
1483: }
1484:
1485: int self_bc = selfOpVariant(i);
1486: if (self_bc >= 0) {
1487: boolean isField = Instruction.isFieldOp(bc);
1488: boolean isSuper = (self_bc >= _self_linker_op
1489: + _self_linker_super _flag);
1490: boolean isAload = prevAload;
1491: prevAload = false; //used up
1492: if (isAload)
1493: self_bc += _self_linker_aload_flag;
1494: // Write special bytecode.
1495: bc_codes.putByte(self_bc);
1496: codeHist[self_bc]++;
1497: // Write field or method ref to a separate band.
1498: MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap);
1499: CPRefBand bc_which = selfOpRefBand(self_bc);
1500: Index which_ix = cp.getMemberIndex(ref.tag,
1501: ref.classRef);
1502: bc_which.putRef(ref, which_ix);
1503: continue;
1504: }
1505: assert (!prevAload);
1506: // End "bc_linker" compression.
1507:
1508: // Normal bytecode.
1509: codeHist[bc]++;
1510: switch (bc) {
1511: case _tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label))
1512: case _lookupswitch: // apc: (df, nc, nc*(case, label))
1513: bc_codes.putByte(bc);
1514: Instruction.Switch isw = (Instruction.Switch) i;
1515: // Note that we do not write the alignment bytes.
1516: int apc = isw.getAlignedPC();
1517: int npc = isw.getNextPC();
1518: // write a length specification into the bytecode stream
1519: int caseCount = isw.getCaseCount();
1520: bc_case_count.putInt(caseCount);
1521: putLabel(bc_label, code, i.getPC(), isw
1522: .getDefaultLabel());
1523: for (int j = 0; j < caseCount; j++) {
1524: putLabel(bc_label, code, i.getPC(), isw
1525: .getCaseLabel(j));
1526: }
1527: // Transmit case values in their own band.
1528: if (bc == _tableswitch) {
1529: bc_case_value.putInt(isw.getCaseValue(0));
1530: } else {
1531: for (int j = 0; j < caseCount; j++) {
1532: bc_case_value.putInt(isw.getCaseValue(j));
1533: }
1534: }
1535: // Done with the switch.
1536: continue;
1537: }
1538:
1539: switch (bc) {
1540: case _xxxunusedxxx: // %%% pretend this is invokedynamic
1541: {
1542: i.setNonstandardLength(3);
1543: int refx = i.getShortAt(1);
1544: Entry ref = (refx == 0) ? null : curCPMap[refx];
1545: // transmit the opcode, carefully:
1546: bc_codes.putByte(_byte_escape);
1547: bc_escsize.putInt(1); // one byte of opcode
1548: bc_escbyte.putByte(bc); // the opcode
1549: // transmit the CP reference, carefully:
1550: bc_codes.putByte(_ref_escape);
1551: bc_escrefsize.putInt(2); // two bytes of ref
1552: bc_escref.putRef(ref); // the ref
1553: continue;
1554: }
1555: }
1556:
1557: int branch = i.getBranchLabel();
1558: if (branch >= 0) {
1559: bc_codes.putByte(bc);
1560: putLabel(bc_label, code, i.getPC(), branch);
1561: continue;
1562: }
1563: Entry ref = i.getCPRef(curCPMap);
1564: if (ref != null) {
1565: if (bc == _new)
1566: newClass = ref;
1567: if (bc == _ldc)
1568: ldcHist[ref.tag]++;
1569: CPRefBand bc_which;
1570: int vbc = bc;
1571: switch (i.getCPTag()) {
1572: case CONSTANT_Literal:
1573: switch (ref.tag) {
1574: case CONSTANT_Integer:
1575: bc_which = bc_intref;
1576: switch (bc) {
1577: case _ldc:
1578: vbc = _ildc;
1579: break;
1580: case _ldc_w:
1581: vbc = _ildc_w;
1582: break;
1583: default:
1584: assert (false);
1585: }
1586: break;
1587: case CONSTANT_Float:
1588: bc_which = bc_floatref;
1589: switch (bc) {
1590: case _ldc:
1591: vbc = _fldc;
1592: break;
1593: case _ldc_w:
1594: vbc = _fldc_w;
1595: break;
1596: default:
1597: assert (false);
1598: }
1599: break;
1600: case CONSTANT_Long:
1601: bc_which = bc_longref;
1602: assert (bc == _ldc2_w);
1603: vbc = _lldc2_w;
1604: break;
1605: case CONSTANT_Double:
1606: bc_which = bc_doubleref;
1607: assert (bc == _ldc2_w);
1608: vbc = _dldc2_w;
1609: break;
1610: case CONSTANT_String:
1611: bc_which = bc_stringref;
1612: switch (bc) {
1613: case _ldc:
1614: vbc = _aldc;
1615: break;
1616: case _ldc_w:
1617: vbc = _aldc_w;
1618: break;
1619: default:
1620: assert (false);
1621: }
1622: break;
1623: case CONSTANT_Class:
1624: bc_which = bc_classref;
1625: switch (bc) {
1626: case _ldc:
1627: vbc = _cldc;
1628: break;
1629: case _ldc_w:
1630: vbc = _cldc_w;
1631: break;
1632: default:
1633: assert (false);
1634: }
1635: break;
1636: default:
1637: bc_which = null;
1638: assert (false);
1639: }
1640: break;
1641: case CONSTANT_Class:
1642: // Use a special shorthand for the current class:
1643: if (ref == curClass.this Class)
1644: ref = null;
1645: bc_which = bc_classref;
1646: break;
1647: case CONSTANT_Fieldref:
1648: bc_which = bc_fieldref;
1649: break;
1650: case CONSTANT_Methodref:
1651: bc_which = bc_methodref;
1652: break;
1653: case CONSTANT_InterfaceMethodref:
1654: bc_which = bc_imethodref;
1655: break;
1656: default:
1657: bc_which = null;
1658: assert (false);
1659: }
1660: bc_codes.putByte(vbc);
1661: bc_which.putRef(ref);
1662: // handle trailing junk
1663: if (bc == _multianewarray) {
1664: assert (i.getConstant() == code
1665: .getByte(i.getPC() + 3));
1666: // Just dump the byte into the bipush pile
1667: bc_byte.putByte(0xFF & i.getConstant());
1668: } else if (bc == _invokeinterface) {
1669: assert (i.getLength() == 5);
1670: // Make sure the discarded bytes are sane:
1671: assert (i.getConstant() == (1 + ((MemberEntry) ref).descRef.typeRef
1672: .computeSize(true)) << 8);
1673: } else {
1674: // Make sure there is nothing else to write.
1675: assert (i.getLength() == ((bc == _ldc) ? 2 : 3));
1676: }
1677: continue;
1678: }
1679: int slot = i.getLocalSlot();
1680: if (slot >= 0) {
1681: bc_codes.putByte(bc);
1682: bc_local.putInt(slot);
1683: int con = i.getConstant();
1684: if (bc == _iinc) {
1685: if (!i.isWide()) {
1686: bc_byte.putByte(0xFF & con);
1687: } else {
1688: bc_short.putInt(0xFFFF & con);
1689: }
1690: } else {
1691: assert (con == 0);
1692: }
1693: continue;
1694: }
1695: // Generic instruction. Copy the body.
1696: bc_codes.putByte(bc);
1697: int pc = i.getPC() + 1;
1698: int npc = i.getNextPC();
1699: if (pc < npc) {
1700: // Do a few remaining multi-byte instructions.
1701: switch (bc) {
1702: case _sipush:
1703: bc_short.putInt(0xFFFF & i.getConstant());
1704: break;
1705: case _bipush:
1706: bc_byte.putByte(0xFF & i.getConstant());
1707: break;
1708: case _newarray:
1709: bc_byte.putByte(0xFF & i.getConstant());
1710: break;
1711: default:
1712: assert (false); // that's it
1713: }
1714: }
1715: }
1716: bc_codes.putByte(_end_marker);
1717: bc_codes.elementCountForDebug++;
1718: codeHist[_end_marker]++;
1719: endCode();
1720: }
1721:
1722: int[] codeHist = new int[1 << 8];
1723: int[] ldcHist = new int[20];
1724:
1725: void printCodeHist() {
1726: assert (verbose > 0);
1727: String[] hist = new String[codeHist.length];
1728: int totalBytes = 0;
1729: for (int bc = 0; bc < codeHist.length; bc++) {
1730: totalBytes += codeHist[bc];
1731: }
1732: for (int bc = 0; bc < codeHist.length; bc++) {
1733: if (codeHist[bc] == 0) {
1734: hist[bc] = "";
1735: continue;
1736: }
1737: String iname = Instruction.byteName(bc);
1738: String count = "" + codeHist[bc];
1739: count = " ".substring(count.length()) + count;
1740: String pct = "" + (codeHist[bc] * 10000 / totalBytes);
1741: while (pct.length() < 4)
1742: pct = "0" + pct;
1743: pct = pct.substring(0, pct.length() - 2) + "."
1744: + pct.substring(pct.length() - 2);
1745: hist[bc] = count + " " + pct + "% " + iname;
1746: }
1747: Arrays.sort(hist);
1748: System.out.println("Bytecode histogram [" + totalBytes + "]");
1749: for (int i = hist.length; --i >= 0;) {
1750: if (hist[i] == "")
1751: continue;
1752: System.out.println(hist[i]);
1753: }
1754: for (int tag = 0; tag < ldcHist.length; tag++) {
1755: int count = ldcHist[tag];
1756: if (count == 0)
1757: continue;
1758: System.out.println("ldc " + ConstantPool.tagName(tag) + " "
1759: + count);
1760: }
1761: }
1762: }
|