Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
JDK 11 java.base.jmod - Base Module
JDK 11 java.base.jmod is the JMOD file for JDK 11 Base module.
JDK 11 Base module compiled class files are stored in \fyicenter\jdk-11.0.1\jmods\java.base.jmod.
JDK 11 Base module compiled class files are also linked and stored in the \fyicenter\jdk-11.0.1\lib\modules JImage file.
JDK 11 Base module source code files are stored in \fyicenter\jdk-11.0.1\lib\src.zip\java.base.
You can click and view the content of each source code file in the list below.
✍: FYIcenter
⏎ com/sun/java/util/jar/pack/PackageWriter.java
/* * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.java.util.jar.pack; import com.sun.java.util.jar.pack.ConstantPool.*; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.File; import com.sun.java.util.jar.pack.Package.InnerClass; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static com.sun.java.util.jar.pack.Constants.*; /** * Writer for a package file. * @author John Rose */ class PackageWriter extends BandStructure { Package pkg; OutputStream finalOut; Package.Version packageVersion; PackageWriter(Package pkg, OutputStream out) throws IOException { this.pkg = pkg; this.finalOut = out; // Caller has specified maximum class file version in the package: initHighestClassVersion(pkg.getHighestClassVersion()); } void write() throws IOException { boolean ok = false; try { if (verbose > 0) { Utils.log.info("Setting up constant pool..."); } setup(); if (verbose > 0) { Utils.log.info("Packing..."); } // writeFileHeader() is done last, since it has ultimate counts // writeBandHeaders() is called after all other bands are done writeConstantPool(); writeFiles(); writeAttrDefs(); writeInnerClasses(); writeClassesAndByteCodes(); writeAttrCounts(); if (verbose > 1) printCodeHist(); // choose codings (fill band_headers if needed) if (verbose > 0) { Utils.log.info("Coding..."); } all_bands.chooseBandCodings(); // now we can write the headers: writeFileHeader(); writeAllBandsTo(finalOut); ok = true; } catch (Exception ee) { Utils.log.warning("Error on output: "+ee, ee); //if (verbose > 0) ee.printStackTrace(); // Write partial output only if we are verbose. if (verbose > 0) finalOut.close(); if (ee instanceof IOException) throw (IOException)ee; if (ee instanceof RuntimeException) throw (RuntimeException)ee; throw new Error("error packing", ee); } } Set<Entry> requiredEntries; // for the CP Map<Attribute.Layout, int[]> backCountTable; // for layout callables int[][] attrCounts; // count attr. occurrences void setup() { requiredEntries = new HashSet<>(); setArchiveOptions(); trimClassAttributes(); collectAttributeLayouts(); pkg.buildGlobalConstantPool(requiredEntries); setBandIndexes(); makeNewAttributeBands(); collectInnerClasses(); } /* * Convenience function to choose an archive version based * on the class file versions observed within the archive * or set the user defined version preset via properties. */ void chooseDefaultPackageVersion() throws IOException { if (pkg.packageVersion != null) { packageVersion = pkg.packageVersion; if (verbose > 0) { Utils.log.info("package version overridden with: " + packageVersion); } return; } Package.Version highV = getHighestClassVersion(); // set the package version now if (highV.lessThan(JAVA6_MAX_CLASS_VERSION)) { // There are only old classfiles in this segment or resources packageVersion = JAVA5_PACKAGE_VERSION; } else if (highV.equals(JAVA6_MAX_CLASS_VERSION) || (highV.equals(JAVA7_MAX_CLASS_VERSION) && !pkg.cp.haveExtraTags())) { // force down the package version if we have jdk7 classes without // any Indy references, this is because jdk7 class file (51.0) without // Indy is identical to jdk6 class file (50.0). packageVersion = JAVA6_PACKAGE_VERSION; } else if (highV.equals(JAVA7_MAX_CLASS_VERSION)) { packageVersion = JAVA7_PACKAGE_VERSION; } else { // Normal case. Use the newest archive format, when available packageVersion = JAVA8_PACKAGE_VERSION; } if (verbose > 0) { Utils.log.info("Highest version class file: " + highV + " package version: " + packageVersion); } } void checkVersion() throws IOException { assert(packageVersion != null); if (packageVersion.lessThan(JAVA7_PACKAGE_VERSION)) { // this bit was reserved for future use in previous versions if (testBit(archiveOptions, AO_HAVE_CP_EXTRAS)) { throw new IOException("Format bits for Java 7 must be zero in previous releases"); } } if (testBit(archiveOptions, AO_UNUSED_MBZ)) { throw new IOException("High archive option bits are reserved and must be zero: " + Integer.toHexString(archiveOptions)); } } void setArchiveOptions() { // Decide on some archive options early. // Does not decide on: AO_HAVE_SPECIAL_FORMATS, // AO_HAVE_CP_NUMBERS, AO_HAVE_FILE_HEADERS. // Also, AO_HAVE_FILE_OPTIONS may be forced on later. int minModtime = pkg.default_modtime; int maxModtime = pkg.default_modtime; int minOptions = -1; int maxOptions = 0; // Import defaults from package (deflate hint, etc.). archiveOptions |= pkg.default_options; for (File file : pkg.files) { int modtime = file.modtime; int options = file.options; if (minModtime == NO_MODTIME) { minModtime = maxModtime = modtime; } else { if (minModtime > modtime) minModtime = modtime; if (maxModtime < modtime) maxModtime = modtime; } minOptions &= options; maxOptions |= options; } if (pkg.default_modtime == NO_MODTIME) { // Make everything else be a positive offset from here. pkg.default_modtime = minModtime; } if (minModtime != NO_MODTIME && minModtime != maxModtime) { // Put them into a band. archiveOptions |= AO_HAVE_FILE_MODTIME; } // If the archive deflation is set do not bother with each file. if (!testBit(archiveOptions,AO_DEFLATE_HINT) && minOptions != -1) { if (testBit(minOptions, FO_DEFLATE_HINT)) { // Every file has the deflate_hint set. // Set it for the whole archive, and omit options. archiveOptions |= AO_DEFLATE_HINT; minOptions -= FO_DEFLATE_HINT; maxOptions -= FO_DEFLATE_HINT; } pkg.default_options |= minOptions; if (minOptions != maxOptions || minOptions != pkg.default_options) { archiveOptions |= AO_HAVE_FILE_OPTIONS; } } // Decide on default version number (majority rule). Map<Package.Version, int[]> verCounts = new HashMap<>(); int bestCount = 0; Package.Version bestVersion = null; for (Class cls : pkg.classes) { Package.Version version = cls.getVersion(); int[] var = verCounts.get(version); if (var == null) { var = new int[1]; verCounts.put(version, var); } int count = (var[0] += 1); //System.out.println("version="+version+" count="+count); if (bestCount < count) { bestCount = count; bestVersion = version; } } verCounts.clear(); if (bestVersion == null) bestVersion = JAVA_MIN_CLASS_VERSION; // degenerate case pkg.defaultClassVersion = bestVersion; if (verbose > 0) Utils.log.info("Consensus version number in segment is " + bestVersion); if (verbose > 0) Utils.log.info("Highest version number in segment is " + pkg.getHighestClassVersion()); // Now add explicit pseudo-attrs. to classes with odd versions. for (Class cls : pkg.classes) { if (!cls.getVersion().equals(bestVersion)) { Attribute a = makeClassFileVersionAttr(cls.getVersion()); if (verbose > 1) { Utils.log.fine("Version "+cls.getVersion() + " of " + cls + " doesn't match package version " + bestVersion); } // Note: Does not add in "natural" order. (Who cares?) cls.addAttribute(a); } } // Decide if we are transmitting a huge resource file: for (File file : pkg.files) { long len = file.getFileLength(); if (len != (int)len) { archiveOptions |= AO_HAVE_FILE_SIZE_HI; if (verbose > 0) Utils.log.info("Note: Huge resource file "+file.getFileName()+" forces 64-bit sizing"); break; } } // Decide if code attributes typically have sub-attributes. // In that case, to preserve compact 1-byte code headers, // we must declare unconditional presence of code flags. int cost0 = 0; int cost1 = 0; for (Class cls : pkg.classes) { for (Class.Method m : cls.getMethods()) { if (m.code != null) { if (m.code.attributeSize() == 0) { // cost of a useless unconditional flags byte cost1 += 1; } else if (shortCodeHeader(m.code) != LONG_CODE_HEADER) { // cost of inflating a short header cost0 += 3; } } } } if (cost0 > cost1) { archiveOptions |= AO_HAVE_ALL_CODE_FLAGS; } if (verbose > 0) Utils.log.info("archiveOptions = " +"0b"+Integer.toBinaryString(archiveOptions)); } void writeFileHeader() throws IOException { chooseDefaultPackageVersion(); writeArchiveMagic(); writeArchiveHeader(); } // Local routine used to format fixed-format scalars // in the file_header: private void putMagicInt32(int val) throws IOException { int res = val; for (int i = 0; i < 4; i++) { archive_magic.putByte(0xFF & (res >>> 24)); res <<= 8; } } void writeArchiveMagic() throws IOException { putMagicInt32(pkg.magic); } void writeArchiveHeader() throws IOException { // for debug only: number of words optimized away int headerSizeForDebug = AH_LENGTH_MIN; // AO_HAVE_SPECIAL_FORMATS is set if non-default // coding techniques are used, or if there are // compressor-defined attributes transmitted. boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); if (!haveSpecial) { haveSpecial |= (band_headers.length() != 0); haveSpecial |= (attrDefsWritten.length != 0); if (haveSpecial) archiveOptions |= AO_HAVE_SPECIAL_FORMATS; } if (haveSpecial) headerSizeForDebug += AH_SPECIAL_FORMAT_LEN; // AO_HAVE_FILE_HEADERS is set if there is any // file or segment envelope information present. boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); if (!haveFiles) { haveFiles |= (archiveNextCount > 0); haveFiles |= (pkg.default_modtime != NO_MODTIME); if (haveFiles) archiveOptions |= AO_HAVE_FILE_HEADERS; } if (haveFiles) headerSizeForDebug += AH_FILE_HEADER_LEN; // AO_HAVE_CP_NUMBERS is set if there are any numbers // in the global constant pool. (Numbers are in 15% of classes.) boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); if (!haveNumbers) { haveNumbers |= pkg.cp.haveNumbers(); if (haveNumbers) archiveOptions |= AO_HAVE_CP_NUMBERS; } if (haveNumbers) headerSizeForDebug += AH_CP_NUMBER_LEN; // AO_HAVE_CP_EXTRAS is set if there are constant pool entries // beyond the Java 6 version of the class file format. boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS); if (!haveCPExtra) { haveCPExtra |= pkg.cp.haveExtraTags(); if (haveCPExtra) archiveOptions |= AO_HAVE_CP_EXTRAS; } if (haveCPExtra) headerSizeForDebug += AH_CP_EXTRA_LEN; // the archiveOptions are all initialized, sanity check now!. checkVersion(); archive_header_0.putInt(packageVersion.minor); archive_header_0.putInt(packageVersion.major); if (verbose > 0) Utils.log.info("Package Version for this segment:" + packageVersion); archive_header_0.putInt(archiveOptions); // controls header format assert(archive_header_0.length() == AH_LENGTH_0); final int DUMMY = 0; if (haveFiles) { assert(archive_header_S.length() == AH_ARCHIVE_SIZE_HI); archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 32) assert(archive_header_S.length() == AH_ARCHIVE_SIZE_LO); archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 0) assert(archive_header_S.length() == AH_LENGTH_S); } // Done with unsized part of header.... if (haveFiles) { archive_header_1.putInt(archiveNextCount); // usually zero archive_header_1.putInt(pkg.default_modtime); archive_header_1.putInt(pkg.files.size()); } else { assert(pkg.files.isEmpty()); } if (haveSpecial) { archive_header_1.putInt(band_headers.length()); archive_header_1.putInt(attrDefsWritten.length); } else { assert(band_headers.length() == 0); assert(attrDefsWritten.length == 0); } writeConstantPoolCounts(haveNumbers, haveCPExtra); archive_header_1.putInt(pkg.getAllInnerClasses().size()); archive_header_1.putInt(pkg.defaultClassVersion.minor); archive_header_1.putInt(pkg.defaultClassVersion.major); archive_header_1.putInt(pkg.classes.size()); // Sanity: Make sure we came out to 29 (less optional fields): assert(archive_header_0.length() + archive_header_S.length() + archive_header_1.length() == headerSizeForDebug); // Figure out all the sizes now, first cut: archiveSize0 = 0; archiveSize1 = all_bands.outputSize(); // Second cut: archiveSize0 += archive_magic.outputSize(); archiveSize0 += archive_header_0.outputSize(); archiveSize0 += archive_header_S.outputSize(); // Make the adjustments: archiveSize1 -= archiveSize0; // Patch the header: if (haveFiles) { int archiveSizeHi = (int)(archiveSize1 >>> 32); int archiveSizeLo = (int)(archiveSize1 >>> 0); archive_header_S.patchValue(AH_ARCHIVE_SIZE_HI, archiveSizeHi); archive_header_S.patchValue(AH_ARCHIVE_SIZE_LO, archiveSizeLo); int zeroLen = UNSIGNED5.getLength(DUMMY); archiveSize0 += UNSIGNED5.getLength(archiveSizeHi) - zeroLen; archiveSize0 += UNSIGNED5.getLength(archiveSizeLo) - zeroLen; } if (verbose > 1) Utils.log.fine("archive sizes: "+ archiveSize0+"+"+archiveSize1); assert(all_bands.outputSize() == archiveSize0+archiveSize1); } void writeConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException { for (byte tag : ConstantPool.TAGS_IN_ORDER) { int count = pkg.cp.getIndexByTag(tag).size(); switch (tag) { case CONSTANT_Utf8: // The null string is always first. if (count > 0) assert(pkg.cp.getIndexByTag(tag).get(0) == ConstantPool.getUtf8Entry("")); break; case CONSTANT_Integer: case CONSTANT_Float: case CONSTANT_Long: case CONSTANT_Double: // Omit counts for numbers if possible. if (!haveNumbers) { assert(count == 0); continue; } break; case CONSTANT_MethodHandle: case CONSTANT_MethodType: case CONSTANT_InvokeDynamic: case CONSTANT_BootstrapMethod: // Omit counts for newer entities if possible. if (!haveCPExtra) { assert(count == 0); continue; } break; } archive_header_1.putInt(count); } } protected Index getCPIndex(byte tag) { return pkg.cp.getIndexByTag(tag); } // (The following observations are out of date; they apply only to // "banding" the constant pool itself. Later revisions of this algorithm // applied the banding technique to every part of the package file, // applying the benefits more broadly.) // Note: Keeping the data separate in passes (or "bands") allows the // compressor to issue significantly shorter indexes for repeated data. // The difference in zipped size is 4%, which is remarkable since the // unzipped sizes are the same (only the byte order differs). // After moving similar data into bands, it becomes natural to delta-encode // each band. (This is especially useful if we sort the constant pool first.) // Delta encoding saves an extra 5% in the output size (13% of the CP itself). // Because a typical delta usees much less data than a byte, the savings after // zipping is even better: A zipped delta-encoded package is 8% smaller than // a zipped non-delta-encoded package. Thus, in the zipped file, a banded, // delta-encoded constant pool saves over 11% (of the total file size) compared // with a zipped unbanded file. void writeConstantPool() throws IOException { IndexGroup cp = pkg.cp; if (verbose > 0) Utils.log.info("Writing CP"); for (byte tag : ConstantPool.TAGS_IN_ORDER) { Index index = cp.getIndexByTag(tag); Entry[] cpMap = index.cpMap; if (verbose > 0) Utils.log.info("Writing "+cpMap.length+" "+ConstantPool.tagName(tag)+" entries..."); if (optDumpBands) { try (PrintStream ps = new PrintStream(getDumpStream(index, ".idx"))) { printArrayTo(ps, cpMap, 0, cpMap.length); } } switch (tag) { case CONSTANT_Utf8: writeUtf8Bands(cpMap); break; case CONSTANT_Integer: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; int x = ((Integer)e.numberValue()).intValue(); cp_Int.putInt(x); } break; case CONSTANT_Float: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; float fx = ((Float)e.numberValue()).floatValue(); int x = Float.floatToIntBits(fx); cp_Float.putInt(x); } break; case CONSTANT_Long: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; long x = ((Long)e.numberValue()).longValue(); cp_Long_hi.putInt((int)(x >>> 32)); cp_Long_lo.putInt((int)(x >>> 0)); } break; case CONSTANT_Double: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; double dx = ((Double)e.numberValue()).doubleValue(); long x = Double.doubleToLongBits(dx); cp_Double_hi.putInt((int)(x >>> 32)); cp_Double_lo.putInt((int)(x >>> 0)); } break; case CONSTANT_String: for (int i = 0; i < cpMap.length; i++) { StringEntry e = (StringEntry) cpMap[i]; cp_String.putRef(e.ref); } break; case CONSTANT_Class: for (int i = 0; i < cpMap.length; i++) { ClassEntry e = (ClassEntry) cpMap[i]; cp_Class.putRef(e.ref); } break; case CONSTANT_Signature: writeSignatureBands(cpMap); break; case CONSTANT_NameandType: for (int i = 0; i < cpMap.length; i++) { DescriptorEntry e = (DescriptorEntry) cpMap[i]; cp_Descr_name.putRef(e.nameRef); cp_Descr_type.putRef(e.typeRef); } break; case CONSTANT_Fieldref: writeMemberRefs(tag, cpMap, cp_Field_class, cp_Field_desc); break; case CONSTANT_Methodref: writeMemberRefs(tag, cpMap, cp_Method_class, cp_Method_desc); break; case CONSTANT_InterfaceMethodref: writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); break; case CONSTANT_MethodHandle: for (int i = 0; i < cpMap.length; i++) { MethodHandleEntry e = (MethodHandleEntry) cpMap[i]; cp_MethodHandle_refkind.putInt(e.refKind); cp_MethodHandle_member.putRef(e.memRef); } break; case CONSTANT_MethodType: for (int i = 0; i < cpMap.length; i++) { MethodTypeEntry e = (MethodTypeEntry) cpMap[i]; cp_MethodType.putRef(e.typeRef); } break; case CONSTANT_InvokeDynamic: for (int i = 0; i < cpMap.length; i++) { InvokeDynamicEntry e = (InvokeDynamicEntry) cpMap[i]; cp_InvokeDynamic_spec.putRef(e.bssRef); cp_InvokeDynamic_desc.putRef(e.descRef); } break; case CONSTANT_BootstrapMethod: for (int i = 0; i < cpMap.length; i++) { BootstrapMethodEntry e = (BootstrapMethodEntry) cpMap[i]; cp_BootstrapMethod_ref.putRef(e.bsmRef); cp_BootstrapMethod_arg_count.putInt(e.argRefs.length); for (Entry argRef : e.argRefs) { cp_BootstrapMethod_arg.putRef(argRef); } } break; default: throw new AssertionError("unexpected CP tag in package"); } } if (optDumpBands || verbose > 1) { for (byte tag = CONSTANT_GroupFirst; tag < CONSTANT_GroupLimit; tag++) { Index index = cp.getIndexByTag(tag); if (index == null || index.isEmpty()) continue; Entry[] cpMap = index.cpMap; if (verbose > 1) Utils.log.info("Index group "+ConstantPool.tagName(tag)+" contains "+cpMap.length+" entries."); if (optDumpBands) { try (PrintStream ps = new PrintStream(getDumpStream(index.debugName, tag, ".gidx", index))) { printArrayTo(ps, cpMap, 0, cpMap.length, true); } } } } } void writeUtf8Bands(Entry[] cpMap) throws IOException { if (cpMap.length == 0) return; // nothing to write // The first element must always be the empty string. assert(cpMap[0].stringValue().equals("")); final int SUFFIX_SKIP_1 = 1; final int PREFIX_SKIP_2 = 2; // Fetch the char arrays, first of all. char[][] chars = new char[cpMap.length][]; for (int i = 0; i < chars.length; i++) { chars[i] = cpMap[i].stringValue().toCharArray(); } // First band: Write lengths of shared prefixes. int[] prefixes = new int[cpMap.length]; // includes 2 skipped zeroes char[] prevChars = {}; for (int i = 0; i < chars.length; i++) { int prefix = 0; char[] curChars = chars[i]; int limit = Math.min(curChars.length, prevChars.length); while (prefix < limit && curChars[prefix] == prevChars[prefix]) prefix++; prefixes[i] = prefix; if (i >= PREFIX_SKIP_2) cp_Utf8_prefix.putInt(prefix); else assert(prefix == 0); prevChars = curChars; } // Second band: Write lengths of unshared suffixes. // Third band: Write the char values in the unshared suffixes. for (int i = 0; i < chars.length; i++) { char[] str = chars[i]; int prefix = prefixes[i]; int suffix = str.length - prefixes[i]; boolean isPacked = false; if (suffix == 0) { // Zero suffix length is special flag to indicate // separate treatment in cp_Utf8_big bands. // This suffix length never occurs naturally, // except in the one case of a zero-length string. // (If it occurs, it is the first, due to sorting.) // The zero length string must, paradoxically, be // encoded as a zero-length cp_Utf8_big band. // This wastes exactly (& tolerably) one null byte. isPacked = (i >= SUFFIX_SKIP_1); // Do not bother to add an empty "(Utf8_big_0)" band. // Also, the initial empty string does not require a band. } else if (optBigStrings && effort > 1 && suffix > 100) { int numWide = 0; for (int n = 0; n < suffix; n++) { if (str[prefix+n] > 127) { numWide++; } } if (numWide > 100) { // Try packing the chars with an alternate encoding. isPacked = tryAlternateEncoding(i, numWide, str, prefix); } } if (i < SUFFIX_SKIP_1) { // No output. assert(!isPacked); assert(suffix == 0); } else if (isPacked) { // Mark packed string with zero-length suffix count. // This tells the unpacker to go elsewhere for the suffix bits. // Fourth band: Write unshared suffix with alternate coding. cp_Utf8_suffix.putInt(0); cp_Utf8_big_suffix.putInt(suffix); } else { assert(suffix != 0); // would be ambiguous // Normal string. Save suffix in third and fourth bands. cp_Utf8_suffix.putInt(suffix); for (int n = 0; n < suffix; n++) { int ch = str[prefix+n]; cp_Utf8_chars.putInt(ch); } } } if (verbose > 0) { int normCharCount = cp_Utf8_chars.length(); int packCharCount = cp_Utf8_big_chars.length(); int charCount = normCharCount + packCharCount; Utils.log.info("Utf8string #CHARS="+charCount+" #PACKEDCHARS="+packCharCount); } } private boolean tryAlternateEncoding(int i, int numWide, char[] str, int prefix) { int suffix = str.length - prefix; int[] cvals = new int[suffix]; for (int n = 0; n < suffix; n++) { cvals[n] = str[prefix+n]; } CodingChooser cc = getCodingChooser(); Coding bigRegular = cp_Utf8_big_chars.regularCoding; String bandName = "(Utf8_big_"+i+")"; int[] sizes = { 0, 0 }; final int BYTE_SIZE = CodingChooser.BYTE_SIZE; final int ZIP_SIZE = CodingChooser.ZIP_SIZE; if (verbose > 1 || cc.verbose > 1) { Utils.log.fine("--- chooseCoding "+bandName); } CodingMethod special = cc.choose(cvals, bigRegular, sizes); Coding charRegular = cp_Utf8_chars.regularCoding; if (verbose > 1) Utils.log.fine("big string["+i+"] len="+suffix+" #wide="+numWide+" size="+sizes[BYTE_SIZE]+"/z="+sizes[ZIP_SIZE]+" coding "+special); if (special != charRegular) { int specialZipSize = sizes[ZIP_SIZE]; int[] normalSizes = cc.computeSize(charRegular, cvals); int normalZipSize = normalSizes[ZIP_SIZE]; int minWin = Math.max(5, normalZipSize/1000); if (verbose > 1) Utils.log.fine("big string["+i+"] normalSize="+normalSizes[BYTE_SIZE]+"/z="+normalSizes[ZIP_SIZE]+" win="+(specialZipSize<normalZipSize-minWin)); if (specialZipSize < normalZipSize-minWin) { IntBand big = cp_Utf8_big_chars.newIntBand(bandName); big.initializeValues(cvals); return true; } } return false; } void writeSignatureBands(Entry[] cpMap) throws IOException { for (int i = 0; i < cpMap.length; i++) { SignatureEntry e = (SignatureEntry) cpMap[i]; cp_Signature_form.putRef(e.formRef); for (int j = 0; j < e.classRefs.length; j++) { cp_Signature_classes.putRef(e.classRefs[j]); } } } void writeMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class, CPRefBand cp_desc) throws IOException { for (int i = 0; i < cpMap.length; i++) { MemberEntry e = (MemberEntry) cpMap[i]; cp_class.putRef(e.classRef); cp_desc.putRef(e.descRef); } } void writeFiles() throws IOException { int numFiles = pkg.files.size(); if (numFiles == 0) return; int options = archiveOptions; boolean haveSizeHi = testBit(options, AO_HAVE_FILE_SIZE_HI); boolean haveModtime = testBit(options, AO_HAVE_FILE_MODTIME); boolean haveOptions = testBit(options, AO_HAVE_FILE_OPTIONS); if (!haveOptions) { for (File file : pkg.files) { if (file.isClassStub()) { haveOptions = true; options |= AO_HAVE_FILE_OPTIONS; archiveOptions = options; break; } } } if (haveSizeHi || haveModtime || haveOptions || !pkg.files.isEmpty()) { options |= AO_HAVE_FILE_HEADERS; archiveOptions = options; } for (File file : pkg.files) { file_name.putRef(file.name); long len = file.getFileLength(); file_size_lo.putInt((int)len); if (haveSizeHi) file_size_hi.putInt((int)(len >>> 32)); if (haveModtime) file_modtime.putInt(file.modtime - pkg.default_modtime); if (haveOptions) file_options.putInt(file.options); file.writeTo(file_bits.collectorStream()); if (verbose > 1) Utils.log.fine("Wrote "+len+" bytes of "+file.name.stringValue()); } if (verbose > 0) Utils.log.info("Wrote "+numFiles+" resource files"); } void collectAttributeLayouts() { maxFlags = new int[ATTR_CONTEXT_LIMIT]; allLayouts = new FixedList<>(ATTR_CONTEXT_LIMIT); for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { allLayouts.set(i, new HashMap<>()); } // Collect maxFlags and allLayouts. for (Class cls : pkg.classes) { visitAttributeLayoutsIn(ATTR_CONTEXT_CLASS, cls); for (Class.Field f : cls.getFields()) { visitAttributeLayoutsIn(ATTR_CONTEXT_FIELD, f); } for (Class.Method m : cls.getMethods()) { visitAttributeLayoutsIn(ATTR_CONTEXT_METHOD, m); if (m.code != null) { visitAttributeLayoutsIn(ATTR_CONTEXT_CODE, m.code); } } } // If there are many species of attributes, use 63-bit flags. for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { int nl = allLayouts.get(i).size(); boolean haveLongFlags = haveFlagsHi(i); final int TOO_MANY_ATTRS = 32 /*int flag size*/ - 12 /*typical flag bits in use*/ + 4 /*typical number of OK overflows*/; if (nl >= TOO_MANY_ATTRS) { // heuristic int mask = 1<<(LG_AO_HAVE_XXX_FLAGS_HI+i); archiveOptions |= mask; haveLongFlags = true; if (verbose > 0) Utils.log.info("Note: Many "+Attribute.contextName(i)+" attributes forces 63-bit flags"); } if (verbose > 1) { Utils.log.fine(Attribute.contextName(i)+".maxFlags = 0x"+Integer.toHexString(maxFlags[i])); Utils.log.fine(Attribute.contextName(i)+".#layouts = "+nl); } assert(haveFlagsHi(i) == haveLongFlags); } initAttrIndexLimit(); // Standard indexes can never conflict with flag bits. Assert it. for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { assert((attrFlagMask[i] & maxFlags[i]) == 0); } // Collect counts for both predefs. and custom defs. // Decide on custom, local attribute definitions. backCountTable = new HashMap<>(); attrCounts = new int[ATTR_CONTEXT_LIMIT][]; for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { // Now the remaining defs in allLayouts[i] need attr. indexes. // Fill up unused flag bits with new defs. // Unused bits are those which are not used by predefined attrs, // and which are always clear in the classfiles. long avHiBits = ~(maxFlags[i] | attrFlagMask[i]); assert(attrIndexLimit[i] > 0); assert(attrIndexLimit[i] < 64); // all bits fit into a Java long avHiBits &= (1L<<attrIndexLimit[i])-1; int nextLoBit = 0; Map<Attribute.Layout, int[]> defMap = allLayouts.get(i); @SuppressWarnings({"unchecked", "rawtypes"}) Map.Entry<Attribute.Layout, int[]>[] layoutsAndCounts = new Map.Entry[defMap.size()]; defMap.entrySet().toArray(layoutsAndCounts); // Sort by count, most frequent first. // Predefs. participate in this sort, though it does not matter. Arrays.sort(layoutsAndCounts, new Comparator<>() { public int compare(Map.Entry<Attribute.Layout, int[]> e0, Map.Entry<Attribute.Layout, int[]> e1) { // Primary sort key is count, reversed. int r = -(e0.getValue()[0] - e1.getValue()[0]); if (r != 0) return r; return e0.getKey().compareTo(e1.getKey()); } }); attrCounts[i] = new int[attrIndexLimit[i]+layoutsAndCounts.length]; for (int j = 0; j < layoutsAndCounts.length; j++) { Map.Entry<Attribute.Layout, int[]> e = layoutsAndCounts[j]; Attribute.Layout def = e.getKey(); int count = e.getValue()[0]; int index; Integer predefIndex = attrIndexTable.get(def); if (predefIndex != null) { // The index is already set. index = predefIndex.intValue(); } else if (avHiBits != 0) { while ((avHiBits & 1) == 0) { avHiBits >>>= 1; nextLoBit += 1; } avHiBits -= 1; // clear low bit; we are using it now // Update attrIndexTable: index = setAttributeLayoutIndex(def, nextLoBit); } else { // Update attrIndexTable: index = setAttributeLayoutIndex(def, ATTR_INDEX_OVERFLOW); } // Now that we know the index, record the count of this def. attrCounts[i][index] = count; // For all callables in the def, keep a tally of back-calls. Attribute.Layout.Element[] cbles = def.getCallables(); final int[] bc = new int[cbles.length]; for (int k = 0; k < cbles.length; k++) { assert(cbles[k].kind == Attribute.EK_CBLE); if (!cbles[k].flagTest(Attribute.EF_BACK)) { bc[k] = -1; // no count to accumulate here } } backCountTable.put(def, bc); if (predefIndex == null) { // Make sure the package CP can name the local attribute. Entry ne = ConstantPool.getUtf8Entry(def.name()); String layout = def.layoutForClassVersion(getHighestClassVersion()); Entry le = ConstantPool.getUtf8Entry(layout); requiredEntries.add(ne); requiredEntries.add(le); if (verbose > 0) { if (index < attrIndexLimit[i]) Utils.log.info("Using free flag bit 1<<"+index+" for "+count+" occurrences of "+def); else Utils.log.info("Using overflow index "+index+" for "+count+" occurrences of "+def); } } } } // Later, when emitting attr_definition_bands, we will look at // attrDefSeen and attrDefs at position 32/63 and beyond. // The attrIndexTable will provide elements of xxx_attr_indexes bands. // Done with scratch variables: maxFlags = null; allLayouts = null; } // Scratch variables for processing attributes and flags. int[] maxFlags; List<Map<Attribute.Layout, int[]>> allLayouts; void visitAttributeLayoutsIn(int ctype, Attribute.Holder h) { // Make note of which flags appear in the class file. // Set them in maxFlags. maxFlags[ctype] |= h.flags; for (Attribute a : h.getAttributes()) { Attribute.Layout def = a.layout(); Map<Attribute.Layout, int[]> defMap = allLayouts.get(ctype); int[] count = defMap.get(def); if (count == null) { defMap.put(def, count = new int[1]); } if (count[0] < Integer.MAX_VALUE) { count[0] += 1; } } } Attribute.Layout[] attrDefsWritten; void writeAttrDefs() throws IOException { List<Object[]> defList = new ArrayList<>(); for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { int limit = attrDefs.get(i).size(); for (int j = 0; j < limit; j++) { int header = i; // ctype if (j < attrIndexLimit[i]) { header |= ((j + ADH_BIT_IS_LSB) << ADH_BIT_SHIFT); assert(header < 0x100); // must fit into a byte // (...else header is simply ctype, with zero high bits.) if (!testBit(attrDefSeen[i], 1L<<j)) { // either undefined or predefined; nothing to write continue; } } Attribute.Layout def = attrDefs.get(i).get(j); defList.add(new Object[]{ Integer.valueOf(header), def }); assert(Integer.valueOf(j).equals(attrIndexTable.get(def))); } } // Sort the new attr defs into some "natural" order. int numAttrDefs = defList.size(); Object[][] defs = new Object[numAttrDefs][]; defList.toArray(defs); Arrays.sort(defs, new Comparator<>() { public int compare(Object[] a0, Object[] a1) { // Primary sort key is attr def header. @SuppressWarnings("unchecked") int r = ((Comparable)a0[0]).compareTo(a1[0]); if (r != 0) return r; Integer ind0 = attrIndexTable.get(a0[1]); Integer ind1 = attrIndexTable.get(a1[1]); // Secondary sort key is attribute index. // (This must be so, in order to keep overflow attr order.) assert(ind0 != null); assert(ind1 != null); return ind0.compareTo(ind1); } }); attrDefsWritten = new Attribute.Layout[numAttrDefs]; try (PrintStream dump = !optDumpBands ? null : new PrintStream(getDumpStream(attr_definition_headers, ".def"))) { int[] indexForDebug = Arrays.copyOf(attrIndexLimit, ATTR_CONTEXT_LIMIT); for (int i = 0; i < defs.length; i++) { int header = ((Integer)defs[i][0]).intValue(); Attribute.Layout def = (Attribute.Layout) defs[i][1]; attrDefsWritten[i] = def; assert((header & ADH_CONTEXT_MASK) == def.ctype()); attr_definition_headers.putByte(header); attr_definition_name.putRef(ConstantPool.getUtf8Entry(def.name())); String layout = def.layoutForClassVersion(getHighestClassVersion()); attr_definition_layout.putRef(ConstantPool.getUtf8Entry(layout)); // Check that we are transmitting that correct attribute index: boolean debug = false; assert(debug = true); if (debug) { int hdrIndex = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB; if (hdrIndex < 0) hdrIndex = indexForDebug[def.ctype()]++; int realIndex = (attrIndexTable.get(def)).intValue(); assert(hdrIndex == realIndex); } if (dump != null) { int index = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB; dump.println(index+" "+def); } } } } void writeAttrCounts() throws IOException { // Write the four xxx_attr_calls bands. for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { MultiBand xxx_attr_bands = attrBands[ctype]; IntBand xxx_attr_calls = getAttrBand(xxx_attr_bands, AB_ATTR_CALLS); Attribute.Layout[] defs = new Attribute.Layout[attrDefs.get(ctype).size()]; attrDefs.get(ctype).toArray(defs); for (boolean predef = true; ; predef = false) { for (int ai = 0; ai < defs.length; ai++) { Attribute.Layout def = defs[ai]; if (def == null) continue; // unused index if (predef != isPredefinedAttr(ctype, ai)) continue; // wrong pass int totalCount = attrCounts[ctype][ai]; if (totalCount == 0) continue; // irrelevant int[] bc = backCountTable.get(def); for (int j = 0; j < bc.length; j++) { if (bc[j] >= 0) { int backCount = bc[j]; bc[j] = -1; // close out; do not collect further counts xxx_attr_calls.putInt(backCount); assert(def.getCallables()[j].flagTest(Attribute.EF_BACK)); } else { assert(!def.getCallables()[j].flagTest(Attribute.EF_BACK)); } } } if (!predef) break; } } } void trimClassAttributes() { for (Class cls : pkg.classes) { // Replace "obvious" SourceFile attrs by null. cls.minimizeSourceFile(); // BootstrapMethods should never have been inserted. assert(cls.getAttribute(Package.attrBootstrapMethodsEmpty) == null); } } void collectInnerClasses() { // Capture inner classes, removing them from individual classes. // Irregular inner classes must stay local, though. Map<ClassEntry, InnerClass> allICMap = new HashMap<>(); // First, collect a consistent global set. for (Class cls : pkg.classes) { if (!cls.hasInnerClasses()) continue; for (InnerClass ic : cls.getInnerClasses()) { InnerClass pic = allICMap.put(ic.thisClass, ic); if (pic != null && !pic.equals(ic) && pic.predictable) { // Different ICs. Choose the better to make global. allICMap.put(pic.thisClass, pic); } } } InnerClass[] allICs = new InnerClass[allICMap.size()]; allICMap.values().toArray(allICs); allICMap = null; // done with it // Note: The InnerClasses attribute must be in a valid order, // so that A$B always occurs earlier than A$B$C. This is an // important side-effect of sorting lexically by class name. Arrays.sort(allICs); // put in canonical order pkg.setAllInnerClasses(Arrays.asList(allICs)); // Next, empty out of every local set the consistent entries. // Calculate whether there is any remaining need to have a local // set, and whether it needs to be locked. for (Class cls : pkg.classes) { cls.minimizeLocalICs(); } } void writeInnerClasses() throws IOException { for (InnerClass ic : pkg.getAllInnerClasses()) { int flags = ic.flags; assert((flags & ACC_IC_LONG_FORM) == 0); if (!ic.predictable) { flags |= ACC_IC_LONG_FORM; } ic_this_class.putRef(ic.thisClass); ic_flags.putInt(flags); if (!ic.predictable) { ic_outer_class.putRef(ic.outerClass); ic_name.putRef(ic.name); } } } /** If there are any extra InnerClasses entries to write which are * not already implied by the global table, put them into a * local attribute. This is expected to be rare. */ void writeLocalInnerClasses(Class cls) throws IOException { List<InnerClass> localICs = cls.getInnerClasses(); class_InnerClasses_N.putInt(localICs.size()); for(InnerClass ic : localICs) { class_InnerClasses_RC.putRef(ic.thisClass); // Is it redundant with the global version? if (ic.equals(pkg.getGlobalInnerClass(ic.thisClass))) { // A zero flag means copy a global IC here. class_InnerClasses_F.putInt(0); } else { int flags = ic.flags; if (flags == 0) flags = ACC_IC_LONG_FORM; // force it to be non-zero class_InnerClasses_F.putInt(flags); class_InnerClasses_outer_RCN.putRef(ic.outerClass); class_InnerClasses_name_RUN.putRef(ic.name); } } } void writeClassesAndByteCodes() throws IOException { Class[] classes = new Class[pkg.classes.size()]; pkg.classes.toArray(classes); // Note: This code respects the order in which caller put classes. if (verbose > 0) Utils.log.info(" ...scanning "+classes.length+" classes..."); int nwritten = 0; for (int i = 0; i < classes.length; i++) { // Collect the class body, sans bytecodes. Class cls = classes[i]; if (verbose > 1) Utils.log.fine("Scanning "+cls); ClassEntry thisClass = cls.thisClass; ClassEntry superClass = cls.superClass; ClassEntry[] interfaces = cls.interfaces; // Encode rare case of null superClass as thisClass: assert(superClass != thisClass); // bad class file!? if (superClass == null) superClass = thisClass; class_this.putRef(thisClass); class_super.putRef(superClass); class_interface_count.putInt(cls.interfaces.length); for (int j = 0; j < interfaces.length; j++) { class_interface.putRef(interfaces[j]); } writeMembers(cls); writeAttrs(ATTR_CONTEXT_CLASS, cls, cls); nwritten++; if (verbose > 0 && (nwritten % 1000) == 0) Utils.log.info("Have scanned "+nwritten+" classes..."); } } void writeMembers(Class cls) throws IOException { List<Class.Field> fields = cls.getFields(); class_field_count.putInt(fields.size()); for (Class.Field f : fields) { field_descr.putRef(f.getDescriptor()); writeAttrs(ATTR_CONTEXT_FIELD, f, cls); } List<Class.Method> methods = cls.getMethods(); class_method_count.putInt(methods.size()); for (Class.Method m : methods) { method_descr.putRef(m.getDescriptor()); writeAttrs(ATTR_CONTEXT_METHOD, m, cls); assert((m.code != null) == (m.getAttribute(attrCodeEmpty) != null)); if (m.code != null) { writeCodeHeader(m.code); writeByteCodes(m.code); } } } void writeCodeHeader(Code c) throws IOException { boolean attrsOK = testBit(archiveOptions, AO_HAVE_ALL_CODE_FLAGS); int na = c.attributeSize(); int sc = shortCodeHeader(c); if (!attrsOK && na > 0) // We must write flags, and can only do so for long headers. sc = LONG_CODE_HEADER; if (verbose > 2) { int siglen = c.getMethod().getArgumentSize(); Utils.log.fine("Code sizes info "+c.max_stack+" "+c.max_locals+" "+c.getHandlerCount()+" "+siglen+" "+na+(sc > 0 ? " SHORT="+sc : "")); } code_headers.putByte(sc); if (sc == LONG_CODE_HEADER) { code_max_stack.putInt(c.getMaxStack()); code_max_na_locals.putInt(c.getMaxNALocals()); code_handler_count.putInt(c.getHandlerCount()); } else { assert(attrsOK || na == 0); assert(c.getHandlerCount() < shortCodeHeader_h_limit); } writeCodeHandlers(c); if (sc == LONG_CODE_HEADER || attrsOK) writeAttrs(ATTR_CONTEXT_CODE, c, c.thisClass()); } void writeCodeHandlers(Code c) throws IOException { int sum, del; for (int j = 0, jmax = c.getHandlerCount(); j < jmax; j++) { code_handler_class_RCN.putRef(c.handler_class[j]); // null OK // Encode end as offset from start, and catch as offset from end, // because they are strongly correlated. sum = c.encodeBCI(c.handler_start[j]); code_handler_start_P.putInt(sum); del = c.encodeBCI(c.handler_end[j]) - sum; code_handler_end_PO.putInt(del); sum += del; del = c.encodeBCI(c.handler_catch[j]) - sum; code_handler_catch_PO.putInt(del); } } // Generic routines for writing attributes and flags of // classes, fields, methods, and codes. void writeAttrs(int ctype, final Attribute.Holder h, Class cls) throws IOException { MultiBand xxx_attr_bands = attrBands[ctype]; IntBand xxx_flags_hi = getAttrBand(xxx_attr_bands, AB_FLAGS_HI); IntBand xxx_flags_lo = getAttrBand(xxx_attr_bands, AB_FLAGS_LO); boolean haveLongFlags = haveFlagsHi(ctype); assert(attrIndexLimit[ctype] == (haveLongFlags? 63: 32)); if (h.attributes == null) { xxx_flags_lo.putInt(h.flags); // no extra bits to set here if (haveLongFlags) xxx_flags_hi.putInt(0); return; } if (verbose > 3) Utils.log.fine("Transmitting attrs for "+h+" flags="+Integer.toHexString(h.flags)); long flagMask = attrFlagMask[ctype]; // which flags are attr bits? long flagsToAdd = 0; int overflowCount = 0; for (Attribute a : h.attributes) { Attribute.Layout def = a.layout(); int index = (attrIndexTable.get(def)).intValue(); assert(attrDefs.get(ctype).get(index) == def); if (verbose > 3) Utils.log.fine("add attr @"+index+" "+a+" in "+h); if (index < attrIndexLimit[ctype] && testBit(flagMask, 1L<<index)) { if (verbose > 3) Utils.log.fine("Adding flag bit 1<<"+index+" in "+Long.toHexString(flagMask)); assert(!testBit(h.flags, 1L<<index)); flagsToAdd |= (1L<<index); flagMask -= (1L<<index); // do not use this bit twice here } else { // an overflow attr. flagsToAdd |= (1L<<X_ATTR_OVERFLOW); overflowCount += 1; if (verbose > 3) Utils.log.fine("Adding overflow attr #"+overflowCount); IntBand xxx_attr_indexes = getAttrBand(xxx_attr_bands, AB_ATTR_INDEXES); xxx_attr_indexes.putInt(index); // System.out.println("overflow @"+index); } if (def.bandCount == 0) { if (def == attrInnerClassesEmpty) { // Special logic to write this attr. writeLocalInnerClasses((Class) h); continue; } // Empty attr; nothing more to write here. continue; } assert(a.fixups == null); final Band[] ab = attrBandTable.get(def); assert(ab != null); assert(ab.length == def.bandCount); final int[] bc = backCountTable.get(def); assert(bc != null); assert(bc.length == def.getCallables().length); // Write one attribute of type def into ab. if (verbose > 2) Utils.log.fine("writing "+a+" in "+h); boolean isCV = (ctype == ATTR_CONTEXT_FIELD && def == attrConstantValue); if (isCV) setConstantValueIndex((Class.Field)h); a.parse(cls, a.bytes(), 0, a.size(), new Attribute.ValueStream() { public void putInt(int bandIndex, int value) { ((IntBand) ab[bandIndex]).putInt(value); } public void putRef(int bandIndex, Entry ref) { ((CPRefBand) ab[bandIndex]).putRef(ref); } public int encodeBCI(int bci) { Code code = (Code) h; return code.encodeBCI(bci); } public void noteBackCall(int whichCallable) { assert(bc[whichCallable] >= 0); bc[whichCallable] += 1; } }); if (isCV) setConstantValueIndex(null); // clean up } if (overflowCount > 0) { IntBand xxx_attr_count = getAttrBand(xxx_attr_bands, AB_ATTR_COUNT); xxx_attr_count.putInt(overflowCount); } xxx_flags_lo.putInt(h.flags | (int)flagsToAdd); if (haveLongFlags) xxx_flags_hi.putInt((int)(flagsToAdd >>> 32)); else assert((flagsToAdd >>> 32) == 0); assert((h.flags & flagsToAdd) == 0) : (h+".flags=" +Integer.toHexString(h.flags)+"^" +Long.toHexString(flagsToAdd)); } // temporary scratch variables for processing code blocks private Code curCode; private Class curClass; private Entry[] curCPMap; private void beginCode(Code c) { assert(curCode == null); curCode = c; curClass = c.m.thisClass(); curCPMap = c.getCPMap(); } private void endCode() { curCode = null; curClass = null; curCPMap = null; } // Return an _invokeinit_op variant, if the instruction matches one, // else -1. private int initOpVariant(Instruction i, Entry newClass) { if (i.getBC() != _invokespecial) return -1; MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); if ("<init>".equals(ref.descRef.nameRef.stringValue()) == false) return -1; ClassEntry refClass = ref.classRef; if (refClass == curClass.thisClass) return _invokeinit_op+_invokeinit_self_option; if (refClass == curClass.superClass) return _invokeinit_op+_invokeinit_super_option; if (refClass == newClass) return _invokeinit_op+_invokeinit_new_option; return -1; } // Return a _self_linker_op variant, if the instruction matches one, // else -1. private int selfOpVariant(Instruction i) { int bc = i.getBC(); if (!(bc >= _first_linker_op && bc <= _last_linker_op)) return -1; MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); // do not optimize this case, simply fall back to regular coding if ((bc == _invokespecial || bc == _invokestatic) && ref.tagEquals(CONSTANT_InterfaceMethodref)) return -1; ClassEntry refClass = ref.classRef; int self_bc = _self_linker_op + (bc - _first_linker_op); if (refClass == curClass.thisClass) return self_bc; if (refClass == curClass.superClass) return self_bc + _self_linker_super_flag; return -1; } void writeByteCodes(Code code) throws IOException { beginCode(code); IndexGroup cp = pkg.cp; // true if the previous instruction is an aload to absorb boolean prevAload = false; // class of most recent new; helps compress <init> calls Entry newClass = null; for (Instruction i = code.instructionAt(0); i != null; i = i.next()) { // %%% Add a stress mode which issues _ref/_byte_escape. if (verbose > 3) Utils.log.fine(i.toString()); if (i.isNonstandard()) { // Crash and burn with a complaint if there are funny // bytecodes in this class file. String complaint = code.getMethod() +" contains an unrecognized bytecode "+i +"; please use the pass-file option on this class."; Utils.log.warning(complaint); throw new IOException(complaint); } if (i.isWide()) { if (verbose > 1) { Utils.log.fine("_wide opcode in "+code); Utils.log.fine(i.toString()); } bc_codes.putByte(_wide); codeHist[_wide]++; } int bc = i.getBC(); // Begin "bc_linker" compression. if (bc == _aload_0) { // Try to group aload_0 with a following operation. Instruction ni = code.instructionAt(i.getNextPC()); if (selfOpVariant(ni) >= 0) { prevAload = true; continue; } } // Test for <init> invocations: int init_bc = initOpVariant(i, newClass); if (init_bc >= 0) { if (prevAload) { // get rid of it bc_codes.putByte(_aload_0); codeHist[_aload_0]++; prevAload = false; //used up } // Write special bytecode. bc_codes.putByte(init_bc); codeHist[init_bc]++; MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); // Write operand to a separate band. int coding = cp.getOverloadingIndex(ref); bc_initref.putInt(coding); continue; } int self_bc = selfOpVariant(i); if (self_bc >= 0) { boolean isField = Instruction.isFieldOp(bc); boolean isSuper = (self_bc >= _self_linker_op+_self_linker_super_flag); boolean isAload = prevAload; prevAload = false; //used up if (isAload) self_bc += _self_linker_aload_flag; // Write special bytecode. bc_codes.putByte(self_bc); codeHist[self_bc]++; // Write field or method ref to a separate band. MemberEntry ref = (MemberEntry) i.getCPRef(curCPMap); CPRefBand bc_which = selfOpRefBand(self_bc); Index which_ix = cp.getMemberIndex(ref.tag, ref.classRef); bc_which.putRef(ref, which_ix); continue; } assert(!prevAload); // End "bc_linker" compression. // Normal bytecode. codeHist[bc]++; switch (bc) { case _tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label)) case _lookupswitch: // apc: (df, nc, nc*(case, label)) bc_codes.putByte(bc); Instruction.Switch isw = (Instruction.Switch) i; // Note that we do not write the alignment bytes. int apc = isw.getAlignedPC(); int npc = isw.getNextPC(); // write a length specification into the bytecode stream int caseCount = isw.getCaseCount(); bc_case_count.putInt(caseCount); putLabel(bc_label, code, i.getPC(), isw.getDefaultLabel()); for (int j = 0; j < caseCount; j++) { putLabel(bc_label, code, i.getPC(), isw.getCaseLabel(j)); } // Transmit case values in their own band. if (bc == _tableswitch) { bc_case_value.putInt(isw.getCaseValue(0)); } else { for (int j = 0; j < caseCount; j++) { bc_case_value.putInt(isw.getCaseValue(j)); } } // Done with the switch. continue; } int branch = i.getBranchLabel(); if (branch >= 0) { bc_codes.putByte(bc); putLabel(bc_label, code, i.getPC(), branch); continue; } Entry ref = i.getCPRef(curCPMap); if (ref != null) { if (bc == _new) newClass = ref; if (bc == _ldc) ldcHist[ref.tag]++; CPRefBand bc_which; int vbc = bc; switch (i.getCPTag()) { case CONSTANT_LoadableValue: switch (ref.tag) { case CONSTANT_Integer: bc_which = bc_intref; switch (bc) { case _ldc: vbc = _ildc; break; case _ldc_w: vbc = _ildc_w; break; default: assert(false); } break; case CONSTANT_Float: bc_which = bc_floatref; switch (bc) { case _ldc: vbc = _fldc; break; case _ldc_w: vbc = _fldc_w; break; default: assert(false); } break; case CONSTANT_Long: bc_which = bc_longref; assert(bc == _ldc2_w); vbc = _lldc2_w; break; case CONSTANT_Double: bc_which = bc_doubleref; assert(bc == _ldc2_w); vbc = _dldc2_w; break; case CONSTANT_String: bc_which = bc_stringref; switch (bc) { case _ldc: vbc = _sldc; break; case _ldc_w: vbc = _sldc_w; break; default: assert(false); } break; case CONSTANT_Class: bc_which = bc_classref; switch (bc) { case _ldc: vbc = _cldc; break; case _ldc_w: vbc = _cldc_w; break; default: assert(false); } break; default: // CONSTANT_MethodHandle, etc. if (getHighestClassVersion().lessThan(JAVA7_MAX_CLASS_VERSION)) { throw new IOException("bad class file major version for Java 7 ldc"); } bc_which = bc_loadablevalueref; switch (bc) { case _ldc: vbc = _qldc; break; case _ldc_w: vbc = _qldc_w; break; default: assert(false); } } break; case CONSTANT_Class: // Use a special shorthand for the current class: if (ref == curClass.thisClass) ref = null; bc_which = bc_classref; break; case CONSTANT_Fieldref: bc_which = bc_fieldref; break; case CONSTANT_Methodref: if (ref.tagEquals(CONSTANT_InterfaceMethodref)) { if (bc == _invokespecial) vbc = _invokespecial_int; if (bc == _invokestatic) vbc = _invokestatic_int; bc_which = bc_imethodref; } else { bc_which = bc_methodref; } break; case CONSTANT_InterfaceMethodref: bc_which = bc_imethodref; break; case CONSTANT_InvokeDynamic: bc_which = bc_indyref; break; default: bc_which = null; assert(false); } if (ref != null && bc_which.index != null && !bc_which.index.contains(ref)) { // Crash and burn with a complaint if there are funny // references for this bytecode instruction. // Example: invokestatic of a CONSTANT_InterfaceMethodref. String complaint = code.getMethod() + " contains a bytecode " + i + " with an unsupported constant reference; please use the pass-file option on this class."; Utils.log.warning(complaint); throw new IOException(complaint); } bc_codes.putByte(vbc); bc_which.putRef(ref); // handle trailing junk if (bc == _multianewarray) { assert(i.getConstant() == code.getByte(i.getPC()+3)); // Just dump the byte into the bipush pile bc_byte.putByte(0xFF & i.getConstant()); } else if (bc == _invokeinterface) { assert(i.getLength() == 5); // Make sure the discarded bytes are sane: assert(i.getConstant() == (1+((MemberEntry)ref).descRef.typeRef.computeSize(true)) << 8); } else if (bc == _invokedynamic) { if (getHighestClassVersion().lessThan(JAVA7_MAX_CLASS_VERSION)) { throw new IOException("bad class major version for Java 7 invokedynamic"); } assert(i.getLength() == 5); assert(i.getConstant() == 0); // last 2 bytes MBZ } else { // Make sure there is nothing else to write. assert(i.getLength() == ((bc == _ldc)?2:3)); } continue; } int slot = i.getLocalSlot(); if (slot >= 0) { bc_codes.putByte(bc); bc_local.putInt(slot); int con = i.getConstant(); if (bc == _iinc) { if (!i.isWide()) { bc_byte.putByte(0xFF & con); } else { bc_short.putInt(0xFFFF & con); } } else { assert(con == 0); } continue; } // Generic instruction. Copy the body. bc_codes.putByte(bc); int pc = i.getPC()+1; int npc = i.getNextPC(); if (pc < npc) { // Do a few remaining multi-byte instructions. switch (bc) { case _sipush: bc_short.putInt(0xFFFF & i.getConstant()); break; case _bipush: bc_byte.putByte(0xFF & i.getConstant()); break; case _newarray: bc_byte.putByte(0xFF & i.getConstant()); break; default: assert(false); // that's it } } } bc_codes.putByte(_end_marker); bc_codes.elementCountForDebug++; codeHist[_end_marker]++; endCode(); } int[] codeHist = new int[1<<8]; int[] ldcHist = new int[20]; void printCodeHist() { assert(verbose > 0); String[] hist = new String[codeHist.length]; int totalBytes = 0; for (int bc = 0; bc < codeHist.length; bc++) { totalBytes += codeHist[bc]; } for (int bc = 0; bc < codeHist.length; bc++) { if (codeHist[bc] == 0) { hist[bc] = ""; continue; } String iname = Instruction.byteName(bc); String count = "" + codeHist[bc]; count = " ".substring(count.length()) + count; String pct = "" + (codeHist[bc] * 10000 / totalBytes); while (pct.length() < 4) { pct = "0" + pct; } pct = pct.substring(0, pct.length()-2) + "." + pct.substring(pct.length()-2); hist[bc] = count + " " + pct + "% " + iname; } Arrays.sort(hist); System.out.println("Bytecode histogram ["+totalBytes+"]"); for (int i = hist.length; --i >= 0; ) { if ("".equals(hist[i])) continue; System.out.println(hist[i]); } for (int tag = 0; tag < ldcHist.length; tag++) { int count = ldcHist[tag]; if (count == 0) continue; System.out.println("ldc "+ConstantPool.tagName(tag)+" "+count); } } }
⏎ com/sun/java/util/jar/pack/PackageWriter.java
Or download all of them as a single archive file:
File name: java.base-11.0.1-src.zip File size: 8740354 bytes Release date: 2018-11-04 Download
2020-05-29, 242624👍, 0💬
Popular Posts:
Apache Log4j IOStreams is a Log4j API extension that provides numerous classes from java.io that can...
ASM is an all purpose Java bytecode manipulation and analysis framework. It can be used to modify ex...
JDK 8 jconsole.jar is the JAR file for JDK 8 JConsole, which is a graphical monitoring tool to monit...
What Is jtds-1.2.2.jar? jtds-1.2.2.jar is the JAR files of jTDS Java library 1.2.2, which is a JDBC ...
What Is ojdbc7.jar for Oracle 12c R1? ojdbc7.jar for Oracle 12c R1 is the JAR files of ojdbc.jar, JD...