J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2001-2005 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 | |
| 26 | package com.sun.java.util.jar.pack; |
| 27 | |
| 28 | import java.io.*; |
| 29 | import java.util.*; |
| 30 | import java.util.logging.Level; |
| 31 | import com.sun.java.util.jar.pack.Package.Class; |
| 32 | import com.sun.java.util.jar.pack.Package.File; |
| 33 | import com.sun.java.util.jar.pack.Package.InnerClass; |
| 34 | import com.sun.java.util.jar.pack.ConstantPool.*; |
| 35 | |
| 36 | /** |
| 37 | * Writer for a package file. |
| 38 | * @author John Rose |
| 39 | */ |
| 40 | class PackageWriter extends BandStructure { |
| 41 | Package pkg; |
| 42 | OutputStream finalOut; |
| 43 | |
| 44 | PackageWriter(Package pkg, OutputStream out) throws IOException { |
| 45 | this.pkg = pkg; |
| 46 | this.finalOut = out; |
| 47 | // Caller has specified archive version in the package: |
| 48 | initPackageMajver(pkg.package_majver); |
| 49 | } |
| 50 | |
| 51 | void write() throws IOException { |
| 52 | boolean ok = false; |
| 53 | try { |
| 54 | if (verbose > 0) { |
| 55 | Utils.log.info("Setting up constant pool..."); |
| 56 | } |
| 57 | setup(); |
| 58 | |
| 59 | if (verbose > 0) { |
| 60 | Utils.log.info("Packing..."); |
| 61 | } |
| 62 | |
| 63 | // writeFileHeader() is done last, since it has ultimate counts |
| 64 | // writeBandHeaders() is called after all other bands are done |
| 65 | writeConstantPool(); |
| 66 | writeFiles(); |
| 67 | writeAttrDefs(); |
| 68 | writeInnerClasses(); |
| 69 | writeClassesAndByteCodes(); |
| 70 | writeAttrCounts(); |
| 71 | |
| 72 | if (verbose > 1) printCodeHist(); |
| 73 | |
| 74 | // choose codings (fill band_headers if needed) |
| 75 | if (verbose > 0) { |
| 76 | Utils.log.info("Coding..."); |
| 77 | } |
| 78 | all_bands.chooseBandCodings(); |
| 79 | |
| 80 | // now we can write the headers: |
| 81 | writeFileHeader(); |
| 82 | |
| 83 | writeAllBandsTo(finalOut); |
| 84 | |
| 85 | ok = true; |
| 86 | } catch (Exception ee) { |
| 87 | Utils.log.log(Level.WARNING, "Error on output: "+ee, ee); |
| 88 | //if (verbose > 0) ee.printStackTrace(); |
| 89 | // Write partial output only if we are verbose. |
| 90 | if (verbose > 0) finalOut.close(); |
| 91 | if (ee instanceof IOException) throw (IOException)ee; |
| 92 | if (ee instanceof RuntimeException) throw (RuntimeException)ee; |
| 93 | throw new Error("error packing", ee); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | HashSet requiredEntries; // for the CP |
| 98 | HashMap backCountTable; // for layout callables |
| 99 | int[][] attrCounts; // count attr. occurences |
| 100 | |
| 101 | void setup() { |
| 102 | requiredEntries = new HashSet(); |
| 103 | setArchiveOptions(); |
| 104 | trimClassAttributes(); |
| 105 | collectAttributeLayouts(); |
| 106 | pkg.buildGlobalConstantPool(requiredEntries); |
| 107 | setBandIndexes(); |
| 108 | makeNewAttributeBands(); |
| 109 | collectInnerClasses(); |
| 110 | } |
| 111 | |
| 112 | void setArchiveOptions() { |
| 113 | // Decide on some archive options early. |
| 114 | // Does not decide on: AO_HAVE_SPECIAL_FORMATS, |
| 115 | // AO_HAVE_CP_NUMBERS, AO_HAVE_FILE_HEADERS. |
| 116 | // Also, AO_HAVE_FILE_OPTIONS may be forced on later. |
| 117 | int minModtime = pkg.default_modtime; |
| 118 | int maxModtime = pkg.default_modtime; |
| 119 | int minOptions = -1; |
| 120 | int maxOptions = 0; |
| 121 | |
| 122 | // Import defaults from package (deflate hint, etc.). |
| 123 | archiveOptions |= pkg.default_options; |
| 124 | |
| 125 | for (Iterator i = pkg.files.iterator(); i.hasNext(); ) { |
| 126 | File file = (File) i.next(); |
| 127 | |
| 128 | int modtime = file.modtime; |
| 129 | int options = file.options; |
| 130 | |
| 131 | if (minModtime == NO_MODTIME) { |
| 132 | minModtime = maxModtime = modtime; |
| 133 | } else { |
| 134 | if (minModtime > modtime) minModtime = modtime; |
| 135 | if (maxModtime < modtime) maxModtime = modtime; |
| 136 | } |
| 137 | minOptions &= options; |
| 138 | maxOptions |= options; |
| 139 | } |
| 140 | if (pkg.default_modtime == NO_MODTIME) { |
| 141 | // Make everything else be a positive offset from here. |
| 142 | pkg.default_modtime = minModtime; |
| 143 | } |
| 144 | if (minModtime != NO_MODTIME && minModtime != maxModtime) { |
| 145 | // Put them into a band. |
| 146 | archiveOptions |= AO_HAVE_FILE_MODTIME; |
| 147 | } |
| 148 | // If the archive deflation is set do not bother with each file. |
| 149 | if (!testBit(archiveOptions,AO_DEFLATE_HINT) && minOptions != -1) { |
| 150 | if (testBit(minOptions, FO_DEFLATE_HINT)) { |
| 151 | // Every file has the deflate_hint set. |
| 152 | // Set it for the whole archive, and omit options. |
| 153 | archiveOptions |= AO_DEFLATE_HINT; |
| 154 | minOptions -= FO_DEFLATE_HINT; |
| 155 | maxOptions -= FO_DEFLATE_HINT; |
| 156 | } |
| 157 | pkg.default_options |= minOptions; |
| 158 | if (minOptions != maxOptions |
| 159 | || minOptions != pkg.default_options) { |
| 160 | archiveOptions |= AO_HAVE_FILE_OPTIONS; |
| 161 | } |
| 162 | } |
| 163 | // Decide on default version number (majority rule). |
| 164 | HashMap verCounts = new HashMap(); |
| 165 | int bestCount = 0; |
| 166 | int bestVersion = -1; |
| 167 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 168 | Class cls = (Class) i.next(); |
| 169 | int version = cls.getVersion(); |
| 170 | int[] var = (int[]) verCounts.get(new Integer(version)); |
| 171 | if (var == null) { |
| 172 | var = new int[1]; |
| 173 | verCounts.put(new Integer(version), var); |
| 174 | } |
| 175 | int count = (var[0] += 1); |
| 176 | //System.out.println("version="+version+" count="+count); |
| 177 | if (bestCount < count) { |
| 178 | bestCount = count; |
| 179 | bestVersion = version; |
| 180 | } |
| 181 | } |
| 182 | verCounts.clear(); |
| 183 | if (bestVersion == -1) bestVersion = 0; // degenerate case |
| 184 | int bestMajver = (char)(bestVersion >>> 16); |
| 185 | int bestMinver = (char)(bestVersion); |
| 186 | pkg.default_class_majver = (short) bestMajver; |
| 187 | pkg.default_class_minver = (short) bestMinver; |
| 188 | String bestVerStr = Package.versionStringOf(bestMajver, bestMinver); |
| 189 | if (verbose > 0) |
| 190 | Utils.log.info("Consensus version number in segment is "+bestVerStr); |
| 191 | if (verbose > 0) |
| 192 | Utils.log.info("Highest version number in segment is "+ |
| 193 | Package.versionStringOf(pkg.getHighestClassVersion())); |
| 194 | |
| 195 | // Now add explicit pseudo-attrs. to classes with odd versions. |
| 196 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 197 | Class cls = (Class) i.next(); |
| 198 | |
| 199 | if (cls.getVersion() != bestVersion) { |
| 200 | Attribute a = makeClassFileVersionAttr(cls.minver, cls.majver); |
| 201 | if (verbose > 1) { |
| 202 | String clsVer = cls.getVersionString(); |
| 203 | String pkgVer = bestVerStr; |
| 204 | Utils.log.fine("Version "+clsVer+" of "+cls |
| 205 | +" doesn't match package version " |
| 206 | +pkgVer); |
| 207 | } |
| 208 | // Note: Does not add in "natural" order. (Who cares?) |
| 209 | cls.addAttribute(a); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // Decide if we are transmitting a huge resource file: |
| 214 | for (Iterator i = pkg.files.iterator(); i.hasNext(); ) { |
| 215 | File file = (File) i.next(); |
| 216 | long len = file.getFileLength(); |
| 217 | if (len != (int)len) { |
| 218 | archiveOptions |= AO_HAVE_FILE_SIZE_HI; |
| 219 | if (verbose > 0) |
| 220 | Utils.log.info("Note: Huge resource file "+file.getFileName()+" forces 64-bit sizing"); |
| 221 | break; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | // Decide if code attributes typically have sub-attributes. |
| 226 | // In that case, to preserve compact 1-byte code headers, |
| 227 | // we must declare unconditional presence of code flags. |
| 228 | int cost0 = 0; |
| 229 | int cost1 = 0; |
| 230 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 231 | Class cls = (Class) i.next(); |
| 232 | for (Iterator j = cls.getMethods().iterator(); j.hasNext(); ) { |
| 233 | Class.Method m = (Class.Method) j.next(); |
| 234 | if (m.code != null) { |
| 235 | if (m.code.attributeSize() == 0) { |
| 236 | // cost of a useless unconditional flags byte |
| 237 | cost1 += 1; |
| 238 | } else if (shortCodeHeader(m.code) != LONG_CODE_HEADER) { |
| 239 | // cost of inflating a short header |
| 240 | cost0 += 3; |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | if (cost0 > cost1) { |
| 246 | archiveOptions |= AO_HAVE_ALL_CODE_FLAGS; |
| 247 | } |
| 248 | if (verbose > 0) |
| 249 | Utils.log.info("archiveOptions = " |
| 250 | +"0b"+Integer.toBinaryString(archiveOptions)); |
| 251 | } |
| 252 | |
| 253 | void writeFileHeader() throws IOException { |
| 254 | pkg.checkVersion(); |
| 255 | writeArchiveMagic(); |
| 256 | writeArchiveHeader(); |
| 257 | } |
| 258 | |
| 259 | // Local routine used to format fixed-format scalars |
| 260 | // in the file_header: |
| 261 | private void putMagicInt32(int val) throws IOException { |
| 262 | int res = val; |
| 263 | for (int i = 0; i < 4; i++) { |
| 264 | archive_magic.putByte(0xFF & (res >>> 24)); |
| 265 | res <<= 8; |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | void writeArchiveMagic() throws IOException { |
| 270 | putMagicInt32(pkg.magic); |
| 271 | } |
| 272 | |
| 273 | void writeArchiveHeader() throws IOException { |
| 274 | // for debug only: number of words optimized away |
| 275 | int headerDiscountForDebug = 0; |
| 276 | |
| 277 | // AO_HAVE_SPECIAL_FORMATS is set if non-default |
| 278 | // coding techniques are used, or if there are |
| 279 | // compressor-defined attributes transmitted. |
| 280 | boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); |
| 281 | if (!haveSpecial) { |
| 282 | haveSpecial |= (band_headers.length() != 0); |
| 283 | haveSpecial |= (attrDefsWritten.length != 0); |
| 284 | if (haveSpecial) |
| 285 | archiveOptions |= AO_HAVE_SPECIAL_FORMATS; |
| 286 | } |
| 287 | if (!haveSpecial) |
| 288 | headerDiscountForDebug += AH_SPECIAL_FORMAT_LEN; |
| 289 | |
| 290 | // AO_HAVE_FILE_HEADERS is set if there is any |
| 291 | // file or segment envelope information present. |
| 292 | boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); |
| 293 | if (!haveFiles) { |
| 294 | haveFiles |= (archiveNextCount > 0); |
| 295 | haveFiles |= (pkg.default_modtime != NO_MODTIME); |
| 296 | if (haveFiles) |
| 297 | archiveOptions |= AO_HAVE_FILE_HEADERS; |
| 298 | } |
| 299 | if (!haveFiles) |
| 300 | headerDiscountForDebug += AH_FILE_HEADER_LEN; |
| 301 | |
| 302 | // AO_HAVE_CP_NUMBERS is set if there are any numbers |
| 303 | // in the global constant pool. (Numbers are in 15% of classes.) |
| 304 | boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); |
| 305 | if (!haveNumbers) { |
| 306 | haveNumbers |= pkg.cp.haveNumbers(); |
| 307 | if (haveNumbers) |
| 308 | archiveOptions |= AO_HAVE_CP_NUMBERS; |
| 309 | } |
| 310 | if (!haveNumbers) |
| 311 | headerDiscountForDebug += AH_CP_NUMBER_LEN; |
| 312 | |
| 313 | assert(pkg.package_majver > 0); // caller must specify! |
| 314 | archive_header_0.putInt(pkg.package_minver); |
| 315 | archive_header_0.putInt(pkg.package_majver); |
| 316 | if (verbose > 0) |
| 317 | Utils.log.info("Package Version for this segment:"+ |
| 318 | Package.versionStringOf(pkg.getPackageVersion())); |
| 319 | archive_header_0.putInt(archiveOptions); // controls header format |
| 320 | assert(archive_header_0.length() == AH_LENGTH_0); |
| 321 | |
| 322 | final int DUMMY = 0; |
| 323 | if (haveFiles) { |
| 324 | assert(archive_header_S.length() == AH_ARCHIVE_SIZE_HI); |
| 325 | archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 32) |
| 326 | assert(archive_header_S.length() == AH_ARCHIVE_SIZE_LO); |
| 327 | archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 0) |
| 328 | assert(archive_header_S.length() == AH_LENGTH_S); |
| 329 | } |
| 330 | |
| 331 | // Done with unsized part of header.... |
| 332 | |
| 333 | if (haveFiles) { |
| 334 | archive_header_1.putInt(archiveNextCount); // usually zero |
| 335 | archive_header_1.putInt(pkg.default_modtime); |
| 336 | archive_header_1.putInt(pkg.files.size()); |
| 337 | } else { |
| 338 | assert(pkg.files.size() == 0); |
| 339 | } |
| 340 | |
| 341 | if (haveSpecial) { |
| 342 | archive_header_1.putInt(band_headers.length()); |
| 343 | archive_header_1.putInt(attrDefsWritten.length); |
| 344 | } else { |
| 345 | assert(band_headers.length() == 0); |
| 346 | assert(attrDefsWritten.length == 0); |
| 347 | } |
| 348 | |
| 349 | writeConstantPoolCounts(haveNumbers); |
| 350 | |
| 351 | archive_header_1.putInt(pkg.getAllInnerClasses().size()); |
| 352 | archive_header_1.putInt(pkg.default_class_minver); |
| 353 | archive_header_1.putInt(pkg.default_class_majver); |
| 354 | archive_header_1.putInt(pkg.classes.size()); |
| 355 | |
| 356 | // Sanity: Make sure we came out to 26 (less optional fields): |
| 357 | assert(archive_header_0.length() + |
| 358 | archive_header_S.length() + |
| 359 | archive_header_1.length() |
| 360 | == AH_LENGTH - headerDiscountForDebug); |
| 361 | |
| 362 | // Figure out all the sizes now, first cut: |
| 363 | archiveSize0 = 0; |
| 364 | archiveSize1 = all_bands.outputSize(); |
| 365 | // Second cut: |
| 366 | archiveSize0 += archive_magic.outputSize(); |
| 367 | archiveSize0 += archive_header_0.outputSize(); |
| 368 | archiveSize0 += archive_header_S.outputSize(); |
| 369 | // Make the adjustments: |
| 370 | archiveSize1 -= archiveSize0; |
| 371 | |
| 372 | // Patch the header: |
| 373 | if (haveFiles) { |
| 374 | int archiveSizeHi = (int)(archiveSize1 >>> 32); |
| 375 | int archiveSizeLo = (int)(archiveSize1 >>> 0); |
| 376 | archive_header_S.patchValue(AH_ARCHIVE_SIZE_HI, archiveSizeHi); |
| 377 | archive_header_S.patchValue(AH_ARCHIVE_SIZE_LO, archiveSizeLo); |
| 378 | int zeroLen = UNSIGNED5.getLength(DUMMY); |
| 379 | archiveSize0 += UNSIGNED5.getLength(archiveSizeHi) - zeroLen; |
| 380 | archiveSize0 += UNSIGNED5.getLength(archiveSizeLo) - zeroLen; |
| 381 | } |
| 382 | if (verbose > 1) |
| 383 | Utils.log.fine("archive sizes: "+ |
| 384 | archiveSize0+"+"+archiveSize1); |
| 385 | assert(all_bands.outputSize() == archiveSize0+archiveSize1); |
| 386 | } |
| 387 | |
| 388 | void writeConstantPoolCounts(boolean haveNumbers) throws IOException { |
| 389 | for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { |
| 390 | byte tag = ConstantPool.TAGS_IN_ORDER[k]; |
| 391 | int count = pkg.cp.getIndexByTag(tag).size(); |
| 392 | switch (tag) { |
| 393 | case CONSTANT_Utf8: |
| 394 | // The null string is always first. |
| 395 | if (count > 0) |
| 396 | assert(pkg.cp.getIndexByTag(tag).get(0) |
| 397 | == ConstantPool.getUtf8Entry("")); |
| 398 | break; |
| 399 | |
| 400 | case CONSTANT_Integer: |
| 401 | case CONSTANT_Float: |
| 402 | case CONSTANT_Long: |
| 403 | case CONSTANT_Double: |
| 404 | // Omit counts for numbers if possible. |
| 405 | if (!haveNumbers) { |
| 406 | assert(count == 0); |
| 407 | continue; |
| 408 | } |
| 409 | break; |
| 410 | } |
| 411 | archive_header_1.putInt(count); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | protected Index getCPIndex(byte tag) { |
| 416 | return pkg.cp.getIndexByTag(tag); |
| 417 | } |
| 418 | |
| 419 | // (The following observations are out of date; they apply only to |
| 420 | // "banding" the constant pool itself. Later revisions of this algorithm |
| 421 | // applied the banding technique to every part of the package file, |
| 422 | // applying the benefits more broadly.) |
| 423 | |
| 424 | // Note: Keeping the data separate in passes (or "bands") allows the |
| 425 | // compressor to issue significantly shorter indexes for repeated data. |
| 426 | // The difference in zipped size is 4%, which is remarkable since the |
| 427 | // unzipped sizes are the same (only the byte order differs). |
| 428 | |
| 429 | // After moving similar data into bands, it becomes natural to delta-encode |
| 430 | // each band. (This is especially useful if we sort the constant pool first.) |
| 431 | // Delta encoding saves an extra 5% in the output size (13% of the CP itself). |
| 432 | // Because a typical delta usees much less data than a byte, the savings after |
| 433 | // zipping is even better: A zipped delta-encoded package is 8% smaller than |
| 434 | // a zipped non-delta-encoded package. Thus, in the zipped file, a banded, |
| 435 | // delta-encoded constant pool saves over 11% (of the total file size) compared |
| 436 | // with a zipped unbanded file. |
| 437 | |
| 438 | void writeConstantPool() throws IOException { |
| 439 | IndexGroup cp = pkg.cp; |
| 440 | |
| 441 | if (verbose > 0) Utils.log.info("Writing CP"); |
| 442 | |
| 443 | for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { |
| 444 | byte tag = ConstantPool.TAGS_IN_ORDER[k]; |
| 445 | Index index = cp.getIndexByTag(tag); |
| 446 | |
| 447 | Entry[] cpMap = index.cpMap; |
| 448 | if (verbose > 0) |
| 449 | Utils.log.info("Writing "+cpMap.length+" "+ConstantPool.tagName(tag)+" entries..."); |
| 450 | |
| 451 | if (optDumpBands) { |
| 452 | PrintStream ps = new PrintStream(getDumpStream(index, ".idx")); |
| 453 | printArrayTo(ps, cpMap, 0, cpMap.length); |
| 454 | ps.close(); |
| 455 | } |
| 456 | |
| 457 | switch (tag) { |
| 458 | case CONSTANT_Utf8: |
| 459 | writeUtf8Bands(cpMap); |
| 460 | break; |
| 461 | case CONSTANT_Integer: |
| 462 | for (int i = 0; i < cpMap.length; i++) { |
| 463 | NumberEntry e = (NumberEntry) cpMap[i]; |
| 464 | int x = ((Integer)e.numberValue()).intValue(); |
| 465 | cp_Int.putInt(x); |
| 466 | } |
| 467 | break; |
| 468 | case CONSTANT_Float: |
| 469 | for (int i = 0; i < cpMap.length; i++) { |
| 470 | NumberEntry e = (NumberEntry) cpMap[i]; |
| 471 | float fx = ((Float)e.numberValue()).floatValue(); |
| 472 | int x = Float.floatToIntBits(fx); |
| 473 | cp_Float.putInt(x); |
| 474 | } |
| 475 | break; |
| 476 | case CONSTANT_Long: |
| 477 | for (int i = 0; i < cpMap.length; i++) { |
| 478 | NumberEntry e = (NumberEntry) cpMap[i]; |
| 479 | long x = ((Long)e.numberValue()).longValue(); |
| 480 | cp_Long_hi.putInt((int)(x >>> 32)); |
| 481 | cp_Long_lo.putInt((int)(x >>> 0)); |
| 482 | } |
| 483 | break; |
| 484 | case CONSTANT_Double: |
| 485 | for (int i = 0; i < cpMap.length; i++) { |
| 486 | NumberEntry e = (NumberEntry) cpMap[i]; |
| 487 | double dx = ((Double)e.numberValue()).doubleValue(); |
| 488 | long x = Double.doubleToLongBits(dx); |
| 489 | cp_Double_hi.putInt((int)(x >>> 32)); |
| 490 | cp_Double_lo.putInt((int)(x >>> 0)); |
| 491 | } |
| 492 | break; |
| 493 | case CONSTANT_String: |
| 494 | for (int i = 0; i < cpMap.length; i++) { |
| 495 | StringEntry e = (StringEntry) cpMap[i]; |
| 496 | cp_String.putRef(e.ref); |
| 497 | } |
| 498 | break; |
| 499 | case CONSTANT_Class: |
| 500 | for (int i = 0; i < cpMap.length; i++) { |
| 501 | ClassEntry e = (ClassEntry) cpMap[i]; |
| 502 | cp_Class.putRef(e.ref); |
| 503 | } |
| 504 | break; |
| 505 | case CONSTANT_Signature: |
| 506 | writeSignatureBands(cpMap); |
| 507 | break; |
| 508 | case CONSTANT_NameandType: |
| 509 | for (int i = 0; i < cpMap.length; i++) { |
| 510 | DescriptorEntry e = (DescriptorEntry) cpMap[i]; |
| 511 | cp_Descr_name.putRef(e.nameRef); |
| 512 | cp_Descr_type.putRef(e.typeRef); |
| 513 | } |
| 514 | break; |
| 515 | case CONSTANT_Fieldref: |
| 516 | writeMemberRefs(tag, cpMap, cp_Field_class, cp_Field_desc); |
| 517 | break; |
| 518 | case CONSTANT_Methodref: |
| 519 | writeMemberRefs(tag, cpMap, cp_Method_class, cp_Method_desc); |
| 520 | break; |
| 521 | case CONSTANT_InterfaceMethodref: |
| 522 | writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); |
| 523 | break; |
| 524 | default: |
| 525 | assert(false); |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | void writeUtf8Bands(Entry[] cpMap) throws IOException { |
| 531 | if (cpMap.length == 0) |
| 532 | return; // nothing to write |
| 533 | |
| 534 | // The first element must always be the empty string. |
| 535 | assert(cpMap[0].stringValue().equals("")); |
| 536 | final int SUFFIX_SKIP_1 = 1; |
| 537 | final int PREFIX_SKIP_2 = 2; |
| 538 | |
| 539 | // Fetch the char arrays, first of all. |
| 540 | char[][] chars = new char[cpMap.length][]; |
| 541 | for (int i = 0; i < chars.length; i++) { |
| 542 | chars[i] = cpMap[i].stringValue().toCharArray(); |
| 543 | } |
| 544 | |
| 545 | // First band: Write lengths of shared prefixes. |
| 546 | int[] prefixes = new int[cpMap.length]; // includes 2 skipped zeroes |
| 547 | char[] prevChars = {}; |
| 548 | for (int i = 0; i < chars.length; i++) { |
| 549 | int prefix = 0; |
| 550 | char[] curChars = chars[i]; |
| 551 | int limit = Math.min(curChars.length, prevChars.length); |
| 552 | while (prefix < limit && curChars[prefix] == prevChars[prefix]) |
| 553 | prefix++; |
| 554 | prefixes[i] = prefix; |
| 555 | if (i >= PREFIX_SKIP_2) |
| 556 | cp_Utf8_prefix.putInt(prefix); |
| 557 | else |
| 558 | assert(prefix == 0); |
| 559 | prevChars = curChars; |
| 560 | } |
| 561 | |
| 562 | // Second band: Write lengths of unshared suffixes. |
| 563 | // Third band: Write the char values in the unshared suffixes. |
| 564 | for (int i = 0; i < chars.length; i++) { |
| 565 | char[] str = chars[i]; |
| 566 | int prefix = prefixes[i]; |
| 567 | int suffix = str.length - prefixes[i]; |
| 568 | boolean isPacked = false; |
| 569 | if (suffix == 0) { |
| 570 | // Zero suffix length is special flag to indicate |
| 571 | // separate treatment in cp_Utf8_big bands. |
| 572 | // This suffix length never occurs naturally, |
| 573 | // except in the one case of a zero-length string. |
| 574 | // (If it occurs, it is the first, due to sorting.) |
| 575 | // The zero length string must, paradoxically, be |
| 576 | // encoded as a zero-length cp_Utf8_big band. |
| 577 | // This wastes exactly (& tolerably) one null byte. |
| 578 | isPacked = (i >= SUFFIX_SKIP_1); |
| 579 | // Do not bother to add an empty "(Utf8_big_0)" band. |
| 580 | // Also, the initial empty string does not require a band. |
| 581 | } else if (optBigStrings && effort > 1 && suffix > 100) { |
| 582 | int numWide = 0; |
| 583 | for (int n = 0; n < suffix; n++) { |
| 584 | if (str[prefix+n] > 127) { |
| 585 | numWide++; |
| 586 | } |
| 587 | } |
| 588 | if (numWide > 100) { |
| 589 | // Try packing the chars with an alternate encoding. |
| 590 | isPacked = tryAlternateEncoding(i, numWide, str, prefix); |
| 591 | } |
| 592 | } |
| 593 | if (i < SUFFIX_SKIP_1) { |
| 594 | // No output. |
| 595 | assert(!isPacked); |
| 596 | assert(suffix == 0); |
| 597 | } else if (isPacked) { |
| 598 | // Mark packed string with zero-length suffix count. |
| 599 | // This tells the unpacker to go elsewhere for the suffix bits. |
| 600 | // Fourth band: Write unshared suffix with alternate coding. |
| 601 | cp_Utf8_suffix.putInt(0); |
| 602 | cp_Utf8_big_suffix.putInt(suffix); |
| 603 | } else { |
| 604 | assert(suffix != 0); // would be ambiguous |
| 605 | // Normal string. Save suffix in third and fourth bands. |
| 606 | cp_Utf8_suffix.putInt(suffix); |
| 607 | for (int n = 0; n < suffix; n++) { |
| 608 | int ch = str[prefix+n]; |
| 609 | cp_Utf8_chars.putInt(ch); |
| 610 | } |
| 611 | } |
| 612 | } |
| 613 | if (verbose > 0) { |
| 614 | int normCharCount = cp_Utf8_chars.length(); |
| 615 | int packCharCount = cp_Utf8_big_chars.length(); |
| 616 | int charCount = normCharCount + packCharCount; |
| 617 | Utils.log.info("Utf8string #CHARS="+charCount+" #PACKEDCHARS="+packCharCount); |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | private boolean tryAlternateEncoding(int i, int numWide, |
| 622 | char[] str, int prefix) { |
| 623 | int suffix = str.length - prefix; |
| 624 | int[] cvals = new int[suffix]; |
| 625 | for (int n = 0; n < suffix; n++) { |
| 626 | cvals[n] = str[prefix+n]; |
| 627 | } |
| 628 | CodingChooser cc = getCodingChooser(); |
| 629 | Coding bigRegular = cp_Utf8_big_chars.regularCoding; |
| 630 | String bandName = "(Utf8_big_"+i+")"; |
| 631 | int[] sizes = { 0, 0 }; |
| 632 | final int BYTE_SIZE = CodingChooser.BYTE_SIZE; |
| 633 | final int ZIP_SIZE = CodingChooser.ZIP_SIZE; |
| 634 | if (verbose > 1 || cc.verbose > 1) { |
| 635 | Utils.log.fine("--- chooseCoding "+bandName); |
| 636 | } |
| 637 | CodingMethod special = cc.choose(cvals, bigRegular, sizes); |
| 638 | Coding charRegular = cp_Utf8_chars.regularCoding; |
| 639 | if (verbose > 1) |
| 640 | Utils.log.fine("big string["+i+"] len="+suffix+" #wide="+numWide+" size="+sizes[BYTE_SIZE]+"/z="+sizes[ZIP_SIZE]+" coding "+special); |
| 641 | if (special != charRegular) { |
| 642 | int specialZipSize = sizes[ZIP_SIZE]; |
| 643 | int[] normalSizes = cc.computeSize(charRegular, cvals); |
| 644 | int normalZipSize = normalSizes[ZIP_SIZE]; |
| 645 | int minWin = Math.max(5, normalZipSize/1000); |
| 646 | if (verbose > 1) |
| 647 | Utils.log.fine("big string["+i+"] normalSize="+normalSizes[BYTE_SIZE]+"/z="+normalSizes[ZIP_SIZE]+" win="+(specialZipSize<normalZipSize-minWin)); |
| 648 | if (specialZipSize < normalZipSize-minWin) { |
| 649 | IntBand big = cp_Utf8_big_chars.newIntBand(bandName); |
| 650 | big.initializeValues(cvals); |
| 651 | return true; |
| 652 | } |
| 653 | } |
| 654 | return false; |
| 655 | } |
| 656 | |
| 657 | void writeSignatureBands(Entry[] cpMap) throws IOException { |
| 658 | for (int i = 0; i < cpMap.length; i++) { |
| 659 | SignatureEntry e = (SignatureEntry) cpMap[i]; |
| 660 | cp_Signature_form.putRef(e.formRef); |
| 661 | for (int j = 0; j < e.classRefs.length; j++) { |
| 662 | cp_Signature_classes.putRef(e.classRefs[j]); |
| 663 | } |
| 664 | } |
| 665 | } |
| 666 | |
| 667 | void writeMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class, CPRefBand cp_desc) throws IOException { |
| 668 | for (int i = 0; i < cpMap.length; i++) { |
| 669 | MemberEntry e = (MemberEntry) cpMap[i]; |
| 670 | cp_class.putRef(e.classRef); |
| 671 | cp_desc.putRef(e.descRef); |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | void writeFiles() throws IOException { |
| 676 | int numFiles = pkg.files.size(); |
| 677 | if (numFiles == 0) return; |
| 678 | int options = archiveOptions; |
| 679 | boolean haveSizeHi = testBit(options, AO_HAVE_FILE_SIZE_HI); |
| 680 | boolean haveModtime = testBit(options, AO_HAVE_FILE_MODTIME); |
| 681 | boolean haveOptions = testBit(options, AO_HAVE_FILE_OPTIONS); |
| 682 | if (!haveOptions) { |
| 683 | for (Iterator i = pkg.files.iterator(); i.hasNext(); ) { |
| 684 | File file = (File) i.next(); |
| 685 | if (file.isClassStub()) { |
| 686 | haveOptions = true; |
| 687 | options |= AO_HAVE_FILE_OPTIONS; |
| 688 | archiveOptions = options; |
| 689 | break; |
| 690 | } |
| 691 | } |
| 692 | } |
| 693 | if (haveSizeHi || haveModtime || haveOptions || !pkg.files.isEmpty()) { |
| 694 | options |= AO_HAVE_FILE_HEADERS; |
| 695 | archiveOptions = options; |
| 696 | } |
| 697 | |
| 698 | for (Iterator i = pkg.files.iterator(); i.hasNext(); ) { |
| 699 | File file = (File) i.next(); |
| 700 | file_name.putRef(file.name); |
| 701 | long len = file.getFileLength(); |
| 702 | file_size_lo.putInt((int)len); |
| 703 | if (haveSizeHi) |
| 704 | file_size_hi.putInt((int)(len >>> 32)); |
| 705 | if (haveModtime) |
| 706 | file_modtime.putInt(file.modtime - pkg.default_modtime); |
| 707 | if (haveOptions) |
| 708 | file_options.putInt(file.options); |
| 709 | file.writeTo(file_bits.collectorStream()); |
| 710 | if (verbose > 1) |
| 711 | Utils.log.fine("Wrote "+len+" bytes of "+file.name.stringValue()); |
| 712 | } |
| 713 | if (verbose > 0) |
| 714 | Utils.log.info("Wrote "+numFiles+" resource files"); |
| 715 | } |
| 716 | |
| 717 | void collectAttributeLayouts() { |
| 718 | maxFlags = new int[ATTR_CONTEXT_LIMIT]; |
| 719 | allLayouts = new HashMap[ATTR_CONTEXT_LIMIT]; |
| 720 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 721 | allLayouts[i] = new HashMap(); |
| 722 | } |
| 723 | // Collect maxFlags and allLayouts. |
| 724 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 725 | Class cls = (Class) i.next(); |
| 726 | visitAttributeLayoutsIn(ATTR_CONTEXT_CLASS, cls); |
| 727 | for (Iterator j = cls.getFields().iterator(); j.hasNext(); ) { |
| 728 | Class.Field f = (Class.Field) j.next(); |
| 729 | visitAttributeLayoutsIn(ATTR_CONTEXT_FIELD, f); |
| 730 | } |
| 731 | for (Iterator j = cls.getMethods().iterator(); j.hasNext(); ) { |
| 732 | Class.Method m = (Class.Method) j.next(); |
| 733 | visitAttributeLayoutsIn(ATTR_CONTEXT_METHOD, m); |
| 734 | if (m.code != null) { |
| 735 | visitAttributeLayoutsIn(ATTR_CONTEXT_CODE, m.code); |
| 736 | } |
| 737 | } |
| 738 | } |
| 739 | // If there are many species of attributes, use 63-bit flags. |
| 740 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 741 | int nl = allLayouts[i].size(); |
| 742 | boolean haveLongFlags = haveFlagsHi(i); |
| 743 | final int TOO_MANY_ATTRS = 32 /*int flag size*/ |
| 744 | - 12 /*typical flag bits in use*/ |
| 745 | + 4 /*typical number of OK overflows*/; |
| 746 | if (nl >= TOO_MANY_ATTRS) { // heuristic |
| 747 | int mask = 1<<(LG_AO_HAVE_XXX_FLAGS_HI+i); |
| 748 | archiveOptions |= mask; |
| 749 | haveLongFlags = true; |
| 750 | if (verbose > 0) |
| 751 | Utils.log.info("Note: Many "+Attribute.contextName(i)+" attributes forces 63-bit flags"); |
| 752 | } |
| 753 | if (verbose > 1) { |
| 754 | Utils.log.fine(Attribute.contextName(i)+".maxFlags = 0x"+Integer.toHexString(maxFlags[i])); |
| 755 | Utils.log.fine(Attribute.contextName(i)+".#layouts = "+nl); |
| 756 | } |
| 757 | assert(haveFlagsHi(i) == haveLongFlags); |
| 758 | } |
| 759 | initAttrIndexLimit(); |
| 760 | |
| 761 | // Standard indexes can never conflict with flag bits. Assert it. |
| 762 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 763 | assert((attrFlagMask[i] & maxFlags[i]) == 0); |
| 764 | } |
| 765 | // Collect counts for both predefs. and custom defs. |
| 766 | // Decide on custom, local attribute definitions. |
| 767 | backCountTable = new HashMap(); |
| 768 | attrCounts = new int[ATTR_CONTEXT_LIMIT][]; |
| 769 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 770 | // Now the remaining defs in allLayouts[i] need attr. indexes. |
| 771 | // Fill up unused flag bits with new defs. |
| 772 | // Unused bits are those which are not used by predefined attrs, |
| 773 | // and which are always clear in the classfiles. |
| 774 | long avHiBits = ~(maxFlags[i] | attrFlagMask[i]); |
| 775 | assert(attrIndexLimit[i] > 0); |
| 776 | assert(attrIndexLimit[i] < 64); // all bits fit into a Java long |
| 777 | avHiBits &= (1L<<attrIndexLimit[i])-1; |
| 778 | int nextLoBit = 0; |
| 779 | Map.Entry[] layoutsAndCounts = new Map.Entry[allLayouts[i].size()]; |
| 780 | allLayouts[i].entrySet().toArray(layoutsAndCounts); |
| 781 | // Sort by count, most frequent first. |
| 782 | // Predefs. participate in this sort, though it does not matter. |
| 783 | Arrays.sort(layoutsAndCounts, new Comparator() { |
| 784 | public int compare(Object o0, Object o1) { |
| 785 | Map.Entry e0 = (Map.Entry) o0; |
| 786 | Map.Entry e1 = (Map.Entry) o1; |
| 787 | // Primary sort key is count, reversed. |
| 788 | int r = - ( ((int[])e0.getValue())[0] |
| 789 | - ((int[])e1.getValue())[0] ); |
| 790 | if (r != 0) return r; |
| 791 | return ((Comparable)e0.getKey()).compareTo(e1.getKey()); |
| 792 | } |
| 793 | }); |
| 794 | attrCounts[i] = new int[attrIndexLimit[i]+layoutsAndCounts.length]; |
| 795 | for (int j = 0; j < layoutsAndCounts.length; j++) { |
| 796 | Map.Entry e = layoutsAndCounts[j]; |
| 797 | Attribute.Layout def = (Attribute.Layout) e.getKey(); |
| 798 | int count = ((int[])e.getValue())[0]; |
| 799 | int index; |
| 800 | Integer predefIndex = (Integer) attrIndexTable.get(def); |
| 801 | if (predefIndex != null) { |
| 802 | // The index is already set. |
| 803 | index = predefIndex.intValue(); |
| 804 | } else if (avHiBits != 0) { |
| 805 | while ((avHiBits & 1) == 0) { |
| 806 | avHiBits >>>= 1; |
| 807 | nextLoBit += 1; |
| 808 | } |
| 809 | avHiBits -= 1; // clear low bit; we are using it now |
| 810 | // Update attrIndexTable: |
| 811 | index = setAttributeLayoutIndex(def, nextLoBit); |
| 812 | } else { |
| 813 | // Update attrIndexTable: |
| 814 | index = setAttributeLayoutIndex(def, ATTR_INDEX_OVERFLOW); |
| 815 | } |
| 816 | |
| 817 | // Now that we know the index, record the count of this def. |
| 818 | attrCounts[i][index] = count; |
| 819 | |
| 820 | // For all callables in the def, keep a tally of back-calls. |
| 821 | Attribute.Layout.Element[] cbles = def.getCallables(); |
| 822 | final int[] bc = new int[cbles.length]; |
| 823 | for (int k = 0; k < cbles.length; k++) { |
| 824 | assert(cbles[k].kind == Attribute.EK_CBLE); |
| 825 | if (!cbles[k].flagTest(Attribute.EF_BACK)) { |
| 826 | bc[k] = -1; // no count to accumulate here |
| 827 | } |
| 828 | } |
| 829 | backCountTable.put(def, bc); |
| 830 | |
| 831 | if (predefIndex == null) { |
| 832 | // Make sure the package CP can name the local attribute. |
| 833 | Entry ne = ConstantPool.getUtf8Entry(def.name()); |
| 834 | String layout = def.layoutForPackageMajver(getPackageMajver()); |
| 835 | Entry le = ConstantPool.getUtf8Entry(layout); |
| 836 | requiredEntries.add(ne); |
| 837 | requiredEntries.add(le); |
| 838 | if (verbose > 0) { |
| 839 | if (index < attrIndexLimit[i]) |
| 840 | Utils.log.info("Using free flag bit 1<<"+index+" for "+count+" occurrences of "+def); |
| 841 | else |
| 842 | Utils.log.info("Using overflow index "+index+" for "+count+" occurrences of "+def); |
| 843 | } |
| 844 | } |
| 845 | } |
| 846 | } |
| 847 | // Later, when emitting attr_definition_bands, we will look at |
| 848 | // attrDefSeen and attrDefs at position 32/63 and beyond. |
| 849 | // The attrIndexTable will provide elements of xxx_attr_indexes bands. |
| 850 | |
| 851 | // Done with scratch variables: |
| 852 | maxFlags = null; |
| 853 | allLayouts = null; |
| 854 | } |
| 855 | |
| 856 | // Scratch variables for processing attributes and flags. |
| 857 | int[] maxFlags; |
| 858 | HashMap[] allLayouts; |
| 859 | |
| 860 | void visitAttributeLayoutsIn(int ctype, Attribute.Holder h) { |
| 861 | // Make note of which flags appear in the class file. |
| 862 | // Set them in maxFlags. |
| 863 | maxFlags[ctype] |= h.flags; |
| 864 | for (Iterator i = h.getAttributes().iterator(); i.hasNext(); ) { |
| 865 | Attribute a = (Attribute) i.next(); |
| 866 | Attribute.Layout def = a.layout(); |
| 867 | int[] count = (int[]) allLayouts[ctype].get(def); |
| 868 | if (count == null) |
| 869 | allLayouts[ctype].put(def, count = new int[1]); |
| 870 | if (count[0] < Integer.MAX_VALUE) |
| 871 | count[0] += 1; |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | Attribute.Layout[] attrDefsWritten; |
| 876 | |
| 877 | void writeAttrDefs() throws IOException { |
| 878 | ArrayList defList = new ArrayList(); |
| 879 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 880 | int limit = attrDefs[i].size(); |
| 881 | for (int j = 0; j < limit; j++) { |
| 882 | int header = i; // ctype |
| 883 | if (j < attrIndexLimit[i]) { |
| 884 | header |= ((j + ADH_BIT_IS_LSB) << ADH_BIT_SHIFT); |
| 885 | assert(header < 0x100); // must fit into a byte |
| 886 | // (...else header is simply ctype, with zero high bits.) |
| 887 | if (!testBit(attrDefSeen[i], 1L<<j)) { |
| 888 | // either undefined or predefined; nothing to write |
| 889 | continue; |
| 890 | } |
| 891 | } |
| 892 | Attribute.Layout def = (Attribute.Layout) attrDefs[i].get(j); |
| 893 | defList.add(new Object[]{ new Integer(header), def }); |
| 894 | assert(new Integer(j).equals(attrIndexTable.get(def))); |
| 895 | } |
| 896 | } |
| 897 | // Sort the new attr defs into some "natural" order. |
| 898 | int numAttrDefs = defList.size(); |
| 899 | Object[][] defs = new Object[numAttrDefs][]; |
| 900 | defList.toArray(defs); |
| 901 | Arrays.sort(defs, new Comparator() { |
| 902 | public int compare(Object o0, Object o1) { |
| 903 | Object[] a0 = (Object[]) o0; |
| 904 | Object[] a1 = (Object[]) o1; |
| 905 | // Primary sort key is attr def header. |
| 906 | int r = ((Comparable)a0[0]).compareTo(a1[0]); |
| 907 | if (r != 0) return r; |
| 908 | Object ind0 = attrIndexTable.get(a0[1]); |
| 909 | Object ind1 = attrIndexTable.get(a1[1]); |
| 910 | // Secondary sort key is attribute index. |
| 911 | // (This must be so, in order to keep overflow attr order.) |
| 912 | assert(ind0 != null); |
| 913 | assert(ind1 != null); |
| 914 | return ((Comparable)ind0).compareTo(ind1); |
| 915 | } |
| 916 | }); |
| 917 | attrDefsWritten = new Attribute.Layout[numAttrDefs]; |
| 918 | PrintStream dump = !optDumpBands ? null |
| 919 | : new PrintStream(getDumpStream(attr_definition_headers, ".def")); |
| 920 | int[] indexForDebug = new int[ATTR_CONTEXT_LIMIT]; |
| 921 | for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { |
| 922 | indexForDebug[i] = attrIndexLimit[i]; |
| 923 | } |
| 924 | for (int i = 0; i < defs.length; i++) { |
| 925 | int header = ((Integer)defs[i][0]).intValue(); |
| 926 | Attribute.Layout def = (Attribute.Layout) defs[i][1]; |
| 927 | attrDefsWritten[i] = def; |
| 928 | assert((header & ADH_CONTEXT_MASK) == def.ctype()); |
| 929 | attr_definition_headers.putByte(header); |
| 930 | attr_definition_name.putRef(ConstantPool.getUtf8Entry(def.name())); |
| 931 | String layout = def.layoutForPackageMajver(getPackageMajver()); |
| 932 | attr_definition_layout.putRef(ConstantPool.getUtf8Entry(layout)); |
| 933 | // Check that we are transmitting that correct attribute index: |
| 934 | boolean debug = false; |
| 935 | assert(debug = true); |
| 936 | if (debug) { |
| 937 | int hdrIndex = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB; |
| 938 | if (hdrIndex < 0) hdrIndex = indexForDebug[def.ctype()]++; |
| 939 | int realIndex = ((Integer) attrIndexTable.get(def)).intValue(); |
| 940 | assert(hdrIndex == realIndex); |
| 941 | } |
| 942 | if (dump != null) { |
| 943 | int index = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB; |
| 944 | dump.println(index+" "+def); |
| 945 | } |
| 946 | } |
| 947 | if (dump != null) dump.close(); |
| 948 | } |
| 949 | |
| 950 | void writeAttrCounts() throws IOException { |
| 951 | // Write the four xxx_attr_calls bands. |
| 952 | for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { |
| 953 | MultiBand xxx_attr_bands = attrBands[ctype]; |
| 954 | IntBand xxx_attr_calls = getAttrBand(xxx_attr_bands, AB_ATTR_CALLS); |
| 955 | Attribute.Layout[] defs = new Attribute.Layout[attrDefs[ctype].size()]; |
| 956 | attrDefs[ctype].toArray(defs); |
| 957 | for (boolean predef = true; ; predef = false) { |
| 958 | for (int ai = 0; ai < defs.length; ai++) { |
| 959 | Attribute.Layout def = defs[ai]; |
| 960 | if (def == null) continue; // unused index |
| 961 | if (predef != isPredefinedAttr(ctype, ai)) |
| 962 | continue; // wrong pass |
| 963 | int totalCount = attrCounts[ctype][ai]; |
| 964 | if (totalCount == 0) |
| 965 | continue; // irrelevant |
| 966 | int[] bc = (int[]) backCountTable.get(def); |
| 967 | for (int j = 0; j < bc.length; j++) { |
| 968 | if (bc[j] >= 0) { |
| 969 | int backCount = bc[j]; |
| 970 | bc[j] = -1; // close out; do not collect further counts |
| 971 | xxx_attr_calls.putInt(backCount); |
| 972 | assert(def.getCallables()[j].flagTest(Attribute.EF_BACK)); |
| 973 | } else { |
| 974 | assert(!def.getCallables()[j].flagTest(Attribute.EF_BACK)); |
| 975 | } |
| 976 | } |
| 977 | } |
| 978 | if (!predef) break; |
| 979 | } |
| 980 | } |
| 981 | } |
| 982 | |
| 983 | void trimClassAttributes() { |
| 984 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 985 | Class cls = (Class) i.next(); |
| 986 | // Replace "obvious" SourceFile attrs by null. |
| 987 | cls.minimizeSourceFile(); |
| 988 | } |
| 989 | } |
| 990 | |
| 991 | void collectInnerClasses() { |
| 992 | // Capture inner classes, removing them from individual classes. |
| 993 | // Irregular inner classes must stay local, though. |
| 994 | HashMap allICMap = new HashMap(); |
| 995 | // First, collect a consistent global set. |
| 996 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 997 | Class cls = (Class) i.next(); |
| 998 | if (!cls.hasInnerClasses()) continue; |
| 999 | for (Iterator j = cls.getInnerClasses().iterator(); j.hasNext(); ) { |
| 1000 | InnerClass ic = (InnerClass) j.next(); |
| 1001 | InnerClass pic = (InnerClass) allICMap.put(ic.thisClass, ic); |
| 1002 | if (pic != null && !pic.equals(ic) && pic.predictable) { |
| 1003 | // Different ICs. Choose the better to make global. |
| 1004 | allICMap.put(pic.thisClass, pic); |
| 1005 | } |
| 1006 | } |
| 1007 | } |
| 1008 | |
| 1009 | InnerClass[] allICs = new InnerClass[allICMap.size()]; |
| 1010 | allICMap.values().toArray(allICs); |
| 1011 | allICMap = null; // done with it |
| 1012 | |
| 1013 | // Note: The InnerClasses attribute must be in a valid order, |
| 1014 | // so that A$B always occurs earlier than A$B$C. This is an |
| 1015 | // important side-effect of sorting lexically by class name. |
| 1016 | Arrays.sort(allICs); // put in canonical order |
| 1017 | pkg.setAllInnerClasses(Arrays.asList(allICs)); |
| 1018 | |
| 1019 | // Next, empty out of every local set the consistent entries. |
| 1020 | // Calculate whether there is any remaining need to have a local |
| 1021 | // set, and whether it needs to be locked. |
| 1022 | for (Iterator i = pkg.classes.iterator(); i.hasNext(); ) { |
| 1023 | Class cls = (Class) i.next(); |
| 1024 | cls.minimizeLocalICs(); |
| 1025 | } |
| 1026 | } |
| 1027 | |
| 1028 | void writeInnerClasses() throws IOException { |
| 1029 | for (Iterator i = pkg.getAllInnerClasses().iterator(); i.hasNext(); ) { |
| 1030 | InnerClass ic = (InnerClass) i.next(); |
| 1031 | int flags = ic.flags; |
| 1032 | assert((flags & ACC_IC_LONG_FORM) == 0); |
| 1033 | if (!ic.predictable) { |
| 1034 | flags |= ACC_IC_LONG_FORM; |
| 1035 | } |
| 1036 | ic_this_class.putRef(ic.thisClass); |
| 1037 | ic_flags.putInt(flags); |
| 1038 | if (!ic.predictable) { |
| 1039 | ic_outer_class.putRef(ic.outerClass); |
| 1040 | ic_name.putRef(ic.name); |
| 1041 | } |
| 1042 | } |
| 1043 | } |
| 1044 | |
| 1045 | /** If there are any extra InnerClasses entries to write which are |
| 1046 | * not already implied by the global table, put them into a |
| 1047 | * local attribute. This is expected to be rare. |
| 1048 | */ |
| 1049 | void writeLocalInnerClasses(Class cls) throws IOException { |
| 1050 | List localICs = cls.getInnerClasses(); |
| 1051 | class_InnerClasses_N.putInt(localICs.size()); |
| 1052 | for (Iterator i = localICs.iterator(); i.hasNext(); ) { |
| 1053 | InnerClass ic = (InnerClass) i.next(); |
| 1054 | class_InnerClasses_RC.putRef(ic.thisClass); |
| 1055 | // Is it redundant with the global version? |
| 1056 | if (ic.equals(pkg.getGlobalInnerClass(ic.thisClass))) { |
| 1057 | // A zero flag means copy a global IC here. |
| 1058 | class_InnerClasses_F.putInt(0); |
| 1059 | } else { |
| 1060 | int flags = ic.flags; |
| 1061 | if (flags == 0) |
| 1062 | flags = ACC_IC_LONG_FORM; // force it to be non-zero |
| 1063 | class_InnerClasses_F.putInt(flags); |
| 1064 | class_InnerClasses_outer_RCN.putRef(ic.outerClass); |
| 1065 | class_InnerClasses_name_RUN.putRef(ic.name); |
| 1066 | } |
| 1067 | } |
| 1068 | } |
| 1069 | |
| 1070 | void writeClassesAndByteCodes() throws IOException { |
| 1071 | Class[] classes = new Class[pkg.classes.size()]; |
| 1072 | pkg.classes.toArray(classes); |
| 1073 | // Note: This code respects the order in which caller put classes. |
| 1074 | if (verbose > 0) |
| 1075 | Utils.log.info(" ...scanning "+classes.length+" classes..."); |
| 1076 | |
| 1077 | int nwritten = 0; |
| 1078 | for (int i = 0; i < classes.length; i++) { |
| 1079 | // Collect the class body, sans bytecodes. |
| 1080 | Class cls = classes[i]; |
| 1081 | if (verbose > 1) |
| 1082 | Utils.log.fine("Scanning "+cls); |
| 1083 | |
| 1084 | ClassEntry thisClass = cls.thisClass; |
| 1085 | ClassEntry superClass = cls.superClass; |
| 1086 | ClassEntry[] interfaces = cls.interfaces; |
| 1087 | // Encode rare case of null superClass as thisClass: |
| 1088 | assert(superClass != thisClass); // bad class file!? |
| 1089 | if (superClass == null) superClass = thisClass; |
| 1090 | class_this.putRef(thisClass); |
| 1091 | class_super.putRef(superClass); |
| 1092 | class_interface_count.putInt(cls.interfaces.length); |
| 1093 | for (int j = 0; j < interfaces.length; j++) { |
| 1094 | class_interface.putRef(interfaces[j]); |
| 1095 | } |
| 1096 | |
| 1097 | writeMembers(cls); |
| 1098 | writeAttrs(ATTR_CONTEXT_CLASS, cls, cls); |
| 1099 | |
| 1100 | nwritten++; |
| 1101 | if (verbose > 0 && (nwritten % 1000) == 0) |
| 1102 | Utils.log.info("Have scanned "+nwritten+" classes..."); |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | void writeMembers(Class cls) throws IOException { |
| 1107 | List fields = cls.getFields(); |
| 1108 | class_field_count.putInt(fields.size()); |
| 1109 | for (Iterator i = fields.iterator(); i.hasNext(); ) { |
| 1110 | Class.Field f = (Class.Field) i.next(); |
| 1111 | field_descr.putRef(f.getDescriptor()); |
| 1112 | writeAttrs(ATTR_CONTEXT_FIELD, f, cls); |
| 1113 | } |
| 1114 | |
| 1115 | List methods = cls.getMethods(); |
| 1116 | class_method_count.putInt(methods.size()); |
| 1117 | for (Iterator i = methods.iterator(); i.hasNext(); ) { |
| 1118 | Class.Method m = (Class.Method) i.next(); |
| 1119 | method_descr.putRef(m.getDescriptor()); |
| 1120 | writeAttrs(ATTR_CONTEXT_METHOD, m, cls); |
| 1121 | assert((m.code != null) == (m.getAttribute(attrCodeEmpty) != null)); |
| 1122 | if (m.code != null) { |
| 1123 | writeCodeHeader(m.code); |
| 1124 | writeByteCodes(m.code); |
| 1125 | } |
| 1126 | } |
| 1127 | } |
| 1128 | |
| 1129 | void writeCodeHeader(Code c) throws IOException { |
| 1130 | boolean attrsOK = testBit(archiveOptions, AO_HAVE_ALL_CODE_FLAGS); |
| 1131 | int na = c.attributeSize(); |
| 1132 | int sc = shortCodeHeader(c); |
| 1133 | if (!attrsOK && na > 0) |
| 1134 | // We must write flags, and can only do so for long headers. |
| 1135 | sc = LONG_CODE_HEADER; |
| 1136 | if (verbose > 2) { |
| 1137 | int siglen = c.getMethod().getArgumentSize(); |
| 1138 | Utils.log.fine("Code sizes info "+c.max_stack+" "+c.max_locals+" "+c.getHandlerCount()+" "+siglen+" "+na+(sc > 0 ? " SHORT="+sc : "")); |
| 1139 | } |
| 1140 | code_headers.putByte(sc); |
| 1141 | if (sc == LONG_CODE_HEADER) { |
| 1142 | code_max_stack.putInt(c.getMaxStack()); |
| 1143 | code_max_na_locals.putInt(c.getMaxNALocals()); |
| 1144 | code_handler_count.putInt(c.getHandlerCount()); |
| 1145 | } else { |
| 1146 | assert(attrsOK || na == 0); |
| 1147 | assert(c.getHandlerCount() < shortCodeHeader_h_limit); |
| 1148 | } |
| 1149 | writeCodeHandlers(c); |
| 1150 | if (sc == LONG_CODE_HEADER || attrsOK) |
| 1151 | writeAttrs(ATTR_CONTEXT_CODE, c, c.thisClass()); |
| 1152 | } |
| 1153 | |
| 1154 | void writeCodeHandlers(Code c) throws IOException { |
| 1155 | int sum, del; |
| 1156 | for (int j = 0, jmax = c.getHandlerCount(); j < jmax; j++) { |
| 1157 | code_handler_class_RCN.putRef(c.handler_class[j]); // null OK |
| 1158 | // Encode end as offset from start, and catch as offset from end, |
| 1159 | // because they are strongly correlated. |
| 1160 | sum = c.encodeBCI(c.handler_start[j]); |
| 1161 | code_handler_start_P.putInt(sum); |
| 1162 | del = c.encodeBCI(c.handler_end[j]) - sum; |
| 1163 | code_handler_end_PO.putInt(del); |
| 1164 | sum += del; |
| 1165 | del = c.encodeBCI(c.handler_catch[j]) - sum; |
| 1166 | code_handler_catch_PO.putInt(del); |
| 1167 | } |
| 1168 | } |
| 1169 | |
| 1170 | // Generic routines for writing attributes and flags of |
| 1171 | // classes, fields, methods, and codes. |
| 1172 | void writeAttrs(int ctype, |
| 1173 | final Attribute.Holder h, |
| 1174 | Class cls) throws IOException { |
| 1175 | MultiBand xxx_attr_bands = attrBands[ctype]; |
| 1176 | IntBand xxx_flags_hi = getAttrBand(xxx_attr_bands, AB_FLAGS_HI); |
| 1177 | IntBand xxx_flags_lo = getAttrBand(xxx_attr_bands, AB_FLAGS_LO); |
| 1178 | boolean haveLongFlags = haveFlagsHi(ctype); |
| 1179 | assert(attrIndexLimit[ctype] == (haveLongFlags? 63: 32)); |
| 1180 | if (h.attributes == null) { |
| 1181 | xxx_flags_lo.putInt(h.flags); // no extra bits to set here |
| 1182 | if (haveLongFlags) |
| 1183 | xxx_flags_hi.putInt(0); |
| 1184 | return; |
| 1185 | } |
| 1186 | if (verbose > 3) |
| 1187 | Utils.log.fine("Transmitting attrs for "+h+" flags="+Integer.toHexString(h.flags)); |
| 1188 | |
| 1189 | long flagMask = attrFlagMask[ctype]; // which flags are attr bits? |
| 1190 | long flagsToAdd = 0; |
| 1191 | int overflowCount = 0; |
| 1192 | for (ListIterator j = h.attributes.listIterator(); j.hasNext(); ) { |
| 1193 | Attribute a = (Attribute) j.next(); |
| 1194 | Attribute.Layout def = a.layout(); |
| 1195 | int index = ((Integer)attrIndexTable.get(def)).intValue(); |
| 1196 | assert(attrDefs[ctype].get(index) == def); |
| 1197 | if (verbose > 3) |
| 1198 | Utils.log.fine("add attr @"+index+" "+a+" in "+h); |
| 1199 | if (index < attrIndexLimit[ctype] && testBit(flagMask, 1L<<index)) { |
| 1200 | if (verbose > 3) |
| 1201 | Utils.log.fine("Adding flag bit 1<<"+index+" in "+Long.toHexString(flagMask)); |
| 1202 | assert(!testBit(h.flags, 1L<<index)); |
| 1203 | flagsToAdd |= (1L<<index); |
| 1204 | flagMask -= (1L<<index); // do not use this bit twice here |
| 1205 | } else { |
| 1206 | // an overflow attr. |
| 1207 | flagsToAdd |= (1L<<X_ATTR_OVERFLOW); |
| 1208 | overflowCount += 1; |
| 1209 | if (verbose > 3) |
| 1210 | Utils.log.fine("Adding overflow attr #"+overflowCount); |
| 1211 | IntBand xxx_attr_indexes = getAttrBand(xxx_attr_bands, AB_ATTR_INDEXES); |
| 1212 | xxx_attr_indexes.putInt(index); |
| 1213 | // System.out.println("overflow @"+index); |
| 1214 | } |
| 1215 | if (def.bandCount == 0) { |
| 1216 | if (def == attrInnerClassesEmpty) { |
| 1217 | // Special logic to write this attr. |
| 1218 | writeLocalInnerClasses((Class) h); |
| 1219 | continue; |
| 1220 | } |
| 1221 | // Empty attr; nothing more to write here. |
| 1222 | continue; |
| 1223 | } |
| 1224 | assert(a.fixups == null); |
| 1225 | final Band[] ab = (Band[]) attrBandTable.get(def); |
| 1226 | assert(ab != null); |
| 1227 | assert(ab.length == def.bandCount); |
| 1228 | final int[] bc = (int[]) backCountTable.get(def); |
| 1229 | assert(bc != null); |
| 1230 | assert(bc.length == def.getCallables().length); |
| 1231 | // Write one attribute of type def into ab. |
| 1232 | if (verbose > 2) Utils.log.fine("writing "+a+" in "+h); |
| 1233 | boolean isCV = (ctype == ATTR_CONTEXT_FIELD && def == attrConstantValue); |
| 1234 | if (isCV) setConstantValueIndex((Class.Field)h); |
| 1235 | a.parse(cls, a.bytes(), 0, a.size(), |
| 1236 | new Attribute.ValueStream() { |
| 1237 | public void putInt(int bandIndex, int value) { |
| 1238 | ((IntBand) ab[bandIndex]).putInt(value); |
| 1239 | } |
| 1240 | public void putRef(int bandIndex, Entry ref) { |
| 1241 | ((CPRefBand) ab[bandIndex]).putRef(ref); |
| 1242 | } |
| 1243 | public int encodeBCI(int bci) { |
| 1244 | Code code = (Code) h; |
| 1245 | return code.encodeBCI(bci); |
| 1246 | } |
| 1247 | public void noteBackCall(int whichCallable) { |
| 1248 | assert(bc[whichCallable] >= 0); |
| 1249 | bc[whichCallable] += 1; |
| 1250 | } |
| 1251 | }); |
| 1252 | if (isCV) setConstantValueIndex(null); // clean up |
| 1253 | } |
| 1254 | |
| 1255 | if (overflowCount > 0) { |
| 1256 | IntBand xxx_attr_count = getAttrBand(xxx_attr_bands, AB_ATTR_COUNT); |
| 1257 | xxx_attr_count.putInt(overflowCount); |
| 1258 | } |
| 1259 | |
| 1260 | xxx_flags_lo.putInt(h.flags | (int)flagsToAdd); |
| 1261 | if (haveLongFlags) |
| 1262 | xxx_flags_hi.putInt((int)(flagsToAdd >>> 32)); |
| 1263 | else |
| 1264 | assert((flagsToAdd >>> 32) == 0); |
| 1265 | assert((h.flags & flagsToAdd) == 0) |
| 1266 | : (h+".flags=" |
| 1267 | +Integer.toHexString(h.flags)+"^" |
| 1268 | +Long.toHexString(flagsToAdd)); |
| 1269 | } |
| 1270 | |
| 1271 | // temporary scratch variables for processing code blocks |
| 1272 | private Code curCode; |
| 1273 | private Class curClass; |
| 1274 | private Entry[] curCPMap; |
| 1275 | private void beginCode(Code c) { |
| 1276 | assert(curCode == null); |
| 1277 | curCode = c; |
| 1278 | curClass = c.m.thisClass(); |
| 1279 | curCPMap = c.getCPMap(); |
| 1280 | } |
| 1281 | private void endCode() { |
| 1282 | curCode = null; |
| 1283 | curClass = null; |
| 1284 | curCPMap = null; |
| 1285 | } |
| 1286 | |
| 1287 | // Return an _invokeinit_op variant, if the instruction matches one, |
| 1288 | // else -1. |
| 1289 | private int initOpVariant(Instruction i, Entry newClass) { |
| 1290 | if (i.getBC() != _invokespecial) return -1; |
| 1291 | MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); |
| 1292 | if (ref.descRef.nameRef.stringValue() != "<init>") |
| 1293 | return -1; |
| 1294 | ClassEntry refClass = ref.classRef; |
| 1295 | if (refClass == curClass.thisClass) |
| 1296 | return _invokeinit_op+_invokeinit_self_option; |
| 1297 | if (refClass == curClass.superClass) |
| 1298 | return _invokeinit_op+_invokeinit_super_option; |
| 1299 | if (refClass == newClass) |
| 1300 | return _invokeinit_op+_invokeinit_new_option; |
| 1301 | return -1; |
| 1302 | } |
| 1303 | |
| 1304 | // Return a _self_linker_op variant, if the instruction matches one, |
| 1305 | // else -1. |
| 1306 | private int selfOpVariant(Instruction i) { |
| 1307 | int bc = i.getBC(); |
| 1308 | if (!(bc >= _first_linker_op && bc <= _last_linker_op)) return -1; |
| 1309 | MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); |
| 1310 | ClassEntry refClass = ref.classRef; |
| 1311 | int self_bc = _self_linker_op + (bc - _first_linker_op); |
| 1312 | if (refClass == curClass.thisClass) |
| 1313 | return self_bc; |
| 1314 | if (refClass == curClass.superClass) |
| 1315 | return self_bc + _self_linker_super_flag; |
| 1316 | return -1; |
| 1317 | } |
| 1318 | |
| 1319 | void writeByteCodes(Code code) throws IOException { |
| 1320 | beginCode(code); |
| 1321 | IndexGroup cp = pkg.cp; |
| 1322 | |
| 1323 | // true if the previous instruction is an aload to absorb |
| 1324 | boolean prevAload = false; |
| 1325 | |
| 1326 | // class of most recent new; helps compress <init> calls |
| 1327 | Entry newClass = null; |
| 1328 | |
| 1329 | for (Instruction i = code.instructionAt(0); i != null; i = i.next()) { |
| 1330 | // %%% Add a stress mode which issues _ref/_byte_escape. |
| 1331 | if (verbose > 3) Utils.log.fine(i.toString()); |
| 1332 | |
| 1333 | if (i.isNonstandard() |
| 1334 | && (!p200.getBoolean(Utils.COM_PREFIX+"invokedynamic") |
| 1335 | || i.getBC() != _xxxunusedxxx)) { |
| 1336 | // Crash and burn with a complaint if there are funny |
| 1337 | // bytecodes in this class file. |
| 1338 | String complaint = code.getMethod() |
| 1339 | +" contains an unrecognized bytecode "+i |
| 1340 | +"; please use the pass-file option on this class."; |
| 1341 | Utils.log.warning(complaint); |
| 1342 | throw new IOException(complaint); |
| 1343 | } |
| 1344 | |
| 1345 | if (i.isWide()) { |
| 1346 | if (verbose > 1) { |
| 1347 | Utils.log.fine("_wide opcode in "+code); |
| 1348 | Utils.log.fine(i.toString()); |
| 1349 | } |
| 1350 | bc_codes.putByte(_wide); |
| 1351 | codeHist[_wide]++; |
| 1352 | } |
| 1353 | |
| 1354 | int bc = i.getBC(); |
| 1355 | |
| 1356 | // Begin "bc_linker" compression. |
| 1357 | if (bc == _aload_0) { |
| 1358 | // Try to group aload_0 with a following operation. |
| 1359 | Instruction ni = code.instructionAt(i.getNextPC()); |
| 1360 | if (selfOpVariant(ni) >= 0) { |
| 1361 | prevAload = true; |
| 1362 | continue; |
| 1363 | } |
| 1364 | } |
| 1365 | |
| 1366 | // Test for <init> invocations: |
| 1367 | int init_bc = initOpVariant(i, newClass); |
| 1368 | if (init_bc >= 0) { |
| 1369 | if (prevAload) { |
| 1370 | // get rid of it |
| 1371 | bc_codes.putByte(_aload_0); |
| 1372 | codeHist[_aload_0]++; |
| 1373 | prevAload = false; //used up |
| 1374 | } |
| 1375 | // Write special bytecode. |
| 1376 | bc_codes.putByte(init_bc); |
| 1377 | codeHist[init_bc]++; |
| 1378 | MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); |
| 1379 | // Write operand to a separate band. |
| 1380 | int coding = cp.getOverloadingIndex(ref); |
| 1381 | bc_initref.putInt(coding); |
| 1382 | continue; |
| 1383 | } |
| 1384 | |
| 1385 | int self_bc = selfOpVariant(i); |
| 1386 | if (self_bc >= 0) { |
| 1387 | boolean isField = Instruction.isFieldOp(bc); |
| 1388 | boolean isSuper = (self_bc >= _self_linker_op+_self_linker_super_flag); |
| 1389 | boolean isAload = prevAload; |
| 1390 | prevAload = false; //used up |
| 1391 | if (isAload) |
| 1392 | self_bc += _self_linker_aload_flag; |
| 1393 | // Write special bytecode. |
| 1394 | bc_codes.putByte(self_bc); |
| 1395 | codeHist[self_bc]++; |
| 1396 | // Write field or method ref to a separate band. |
| 1397 | MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); |
| 1398 | CPRefBand bc_which = selfOpRefBand(self_bc); |
| 1399 | Index which_ix = cp.getMemberIndex(ref.tag, ref.classRef); |
| 1400 | bc_which.putRef(ref, which_ix); |
| 1401 | continue; |
| 1402 | } |
| 1403 | assert(!prevAload); |
| 1404 | // End "bc_linker" compression. |
| 1405 | |
| 1406 | // Normal bytecode. |
| 1407 | codeHist[bc]++; |
| 1408 | switch (bc) { |
| 1409 | case _tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label)) |
| 1410 | case _lookupswitch: // apc: (df, nc, nc*(case, label)) |
| 1411 | bc_codes.putByte(bc); |
| 1412 | Instruction.Switch isw = (Instruction.Switch) i; |
| 1413 | // Note that we do not write the alignment bytes. |
| 1414 | int apc = isw.getAlignedPC(); |
| 1415 | int npc = isw.getNextPC(); |
| 1416 | // write a length specification into the bytecode stream |
| 1417 | int caseCount = isw.getCaseCount(); |
| 1418 | bc_case_count.putInt(caseCount); |
| 1419 | putLabel(bc_label, code, i.getPC(), isw.getDefaultLabel()); |
| 1420 | for (int j = 0; j < caseCount; j++) { |
| 1421 | putLabel(bc_label, code, i.getPC(), isw.getCaseLabel(j)); |
| 1422 | } |
| 1423 | // Transmit case values in their own band. |
| 1424 | if (bc == _tableswitch) { |
| 1425 | bc_case_value.putInt(isw.getCaseValue(0)); |
| 1426 | } else { |
| 1427 | for (int j = 0; j < caseCount; j++) { |
| 1428 | bc_case_value.putInt(isw.getCaseValue(j)); |
| 1429 | } |
| 1430 | } |
| 1431 | // Done with the switch. |
| 1432 | continue; |
| 1433 | } |
| 1434 | |
| 1435 | switch (bc) { |
| 1436 | case _xxxunusedxxx: // %%% pretend this is invokedynamic |
| 1437 | { |
| 1438 | i.setNonstandardLength(3); |
| 1439 | int refx = i.getShortAt(1); |
| 1440 | Entry ref = (refx == 0)? null: curCPMap[refx]; |
| 1441 | // transmit the opcode, carefully: |
| 1442 | bc_codes.putByte(_byte_escape); |
| 1443 | bc_escsize.putInt(1); // one byte of opcode |
| 1444 | bc_escbyte.putByte(bc); // the opcode |
| 1445 | // transmit the CP reference, carefully: |
| 1446 | bc_codes.putByte(_ref_escape); |
| 1447 | bc_escrefsize.putInt(2); // two bytes of ref |
| 1448 | bc_escref.putRef(ref); // the ref |
| 1449 | continue; |
| 1450 | } |
| 1451 | } |
| 1452 | |
| 1453 | int branch = i.getBranchLabel(); |
| 1454 | if (branch >= 0) { |
| 1455 | bc_codes.putByte(bc); |
| 1456 | putLabel(bc_label, code, i.getPC(), branch); |
| 1457 | continue; |
| 1458 | } |
| 1459 | Entry ref = i.getCPRef(curCPMap); |
| 1460 | if (ref != null) { |
| 1461 | if (bc == _new) newClass = ref; |
| 1462 | if (bc == _ldc) ldcHist[ref.tag]++; |
| 1463 | CPRefBand bc_which; |
| 1464 | int vbc = bc; |
| 1465 | switch (i.getCPTag()) { |
| 1466 | case CONSTANT_Literal: |
| 1467 | switch (ref.tag) { |
| 1468 | case CONSTANT_Integer: |
| 1469 | bc_which = bc_intref; |
| 1470 | switch (bc) { |
| 1471 | case _ldc: vbc = _ildc; break; |
| 1472 | case _ldc_w: vbc = _ildc_w; break; |
| 1473 | default: assert(false); |
| 1474 | } |
| 1475 | break; |
| 1476 | case CONSTANT_Float: |
| 1477 | bc_which = bc_floatref; |
| 1478 | switch (bc) { |
| 1479 | case _ldc: vbc = _fldc; break; |
| 1480 | case _ldc_w: vbc = _fldc_w; break; |
| 1481 | default: assert(false); |
| 1482 | } |
| 1483 | break; |
| 1484 | case CONSTANT_Long: |
| 1485 | bc_which = bc_longref; |
| 1486 | assert(bc == _ldc2_w); |
| 1487 | vbc = _lldc2_w; |
| 1488 | break; |
| 1489 | case CONSTANT_Double: |
| 1490 | bc_which = bc_doubleref; |
| 1491 | assert(bc == _ldc2_w); |
| 1492 | vbc = _dldc2_w; |
| 1493 | break; |
| 1494 | case CONSTANT_String: |
| 1495 | bc_which = bc_stringref; |
| 1496 | switch (bc) { |
| 1497 | case _ldc: vbc = _aldc; break; |
| 1498 | case _ldc_w: vbc = _aldc_w; break; |
| 1499 | default: assert(false); |
| 1500 | } |
| 1501 | break; |
| 1502 | case CONSTANT_Class: |
| 1503 | bc_which = bc_classref; |
| 1504 | switch (bc) { |
| 1505 | case _ldc: vbc = _cldc; break; |
| 1506 | case _ldc_w: vbc = _cldc_w; break; |
| 1507 | default: assert(false); |
| 1508 | } |
| 1509 | break; |
| 1510 | default: |
| 1511 | bc_which = null; |
| 1512 | assert(false); |
| 1513 | } |
| 1514 | break; |
| 1515 | case CONSTANT_Class: |
| 1516 | // Use a special shorthand for the current class: |
| 1517 | if (ref == curClass.thisClass) ref = null; |
| 1518 | bc_which = bc_classref; break; |
| 1519 | case CONSTANT_Fieldref: |
| 1520 | bc_which = bc_fieldref; break; |
| 1521 | case CONSTANT_Methodref: |
| 1522 | bc_which = bc_methodref; break; |
| 1523 | case CONSTANT_InterfaceMethodref: |
| 1524 | bc_which = bc_imethodref; break; |
| 1525 | default: |
| 1526 | bc_which = null; |
| 1527 | assert(false); |
| 1528 | } |
| 1529 | bc_codes.putByte(vbc); |
| 1530 | bc_which.putRef(ref); |
| 1531 | // handle trailing junk |
| 1532 | if (bc == _multianewarray) { |
| 1533 | assert(i.getConstant() == code.getByte(i.getPC()+3)); |
| 1534 | // Just dump the byte into the bipush pile |
| 1535 | bc_byte.putByte(0xFF & i.getConstant()); |
| 1536 | } else if (bc == _invokeinterface) { |
| 1537 | assert(i.getLength() == 5); |
| 1538 | // Make sure the discarded bytes are sane: |
| 1539 | assert(i.getConstant() == (1+((MemberEntry)ref).descRef.typeRef.computeSize(true)) << 8); |
| 1540 | } else { |
| 1541 | // Make sure there is nothing else to write. |
| 1542 | assert(i.getLength() == ((bc == _ldc)?2:3)); |
| 1543 | } |
| 1544 | continue; |
| 1545 | } |
| 1546 | int slot = i.getLocalSlot(); |
| 1547 | if (slot >= 0) { |
| 1548 | bc_codes.putByte(bc); |
| 1549 | bc_local.putInt(slot); |
| 1550 | int con = i.getConstant(); |
| 1551 | if (bc == _iinc) { |
| 1552 | if (!i.isWide()) { |
| 1553 | bc_byte.putByte(0xFF & con); |
| 1554 | } else { |
| 1555 | bc_short.putInt(0xFFFF & con); |
| 1556 | } |
| 1557 | } else { |
| 1558 | assert(con == 0); |
| 1559 | } |
| 1560 | continue; |
| 1561 | } |
| 1562 | // Generic instruction. Copy the body. |
| 1563 | bc_codes.putByte(bc); |
| 1564 | int pc = i.getPC()+1; |
| 1565 | int npc = i.getNextPC(); |
| 1566 | if (pc < npc) { |
| 1567 | // Do a few remaining multi-byte instructions. |
| 1568 | switch (bc) { |
| 1569 | case _sipush: |
| 1570 | bc_short.putInt(0xFFFF & i.getConstant()); |
| 1571 | break; |
| 1572 | case _bipush: |
| 1573 | bc_byte.putByte(0xFF & i.getConstant()); |
| 1574 | break; |
| 1575 | case _newarray: |
| 1576 | bc_byte.putByte(0xFF & i.getConstant()); |
| 1577 | break; |
| 1578 | default: |
| 1579 | assert(false); // that's it |
| 1580 | } |
| 1581 | } |
| 1582 | } |
| 1583 | bc_codes.putByte(_end_marker); |
| 1584 | bc_codes.elementCountForDebug++; |
| 1585 | codeHist[_end_marker]++; |
| 1586 | endCode(); |
| 1587 | } |
| 1588 | |
| 1589 | int[] codeHist = new int[1<<8]; |
| 1590 | int[] ldcHist = new int[20]; |
| 1591 | void printCodeHist() { |
| 1592 | assert(verbose > 0); |
| 1593 | String[] hist = new String[codeHist.length]; |
| 1594 | int totalBytes = 0; |
| 1595 | for (int bc = 0; bc < codeHist.length; bc++) { |
| 1596 | totalBytes += codeHist[bc]; |
| 1597 | } |
| 1598 | for (int bc = 0; bc < codeHist.length; bc++) { |
| 1599 | if (codeHist[bc] == 0) { hist[bc] = ""; continue; } |
| 1600 | String iname = Instruction.byteName(bc); |
| 1601 | String count = "" + codeHist[bc]; |
| 1602 | count = " ".substring(count.length()) + count; |
| 1603 | String pct = "" + (codeHist[bc] * 10000 / totalBytes); |
| 1604 | while (pct.length() < 4) pct = "0" + pct; |
| 1605 | pct = pct.substring(0, pct.length()-2) + "." + pct.substring(pct.length()-2); |
| 1606 | hist[bc] = count + " " + pct + "% " + iname; |
| 1607 | } |
| 1608 | Arrays.sort(hist); |
| 1609 | System.out.println("Bytecode histogram ["+totalBytes+"]"); |
| 1610 | for (int i = hist.length; --i >= 0; ) { |
| 1611 | if (hist[i] == "") continue; |
| 1612 | System.out.println(hist[i]); |
| 1613 | } |
| 1614 | for (int tag = 0; tag < ldcHist.length; tag++) { |
| 1615 | int count = ldcHist[tag]; |
| 1616 | if (count == 0) continue; |
| 1617 | System.out.println("ldc "+ConstantPool.tagName(tag)+" "+count); |
| 1618 | } |
| 1619 | } |
| 1620 | } |