OpenJDK / amber / amber
changeset 59239:f236fd5d0c2c
8234542: code removal of Pack200 Tools and API
Reviewed-by: alanb, mchung, erikj
author | henryjen |
---|---|
date | Tue, 10 Dec 2019 00:36:30 +0000 |
parents | 2aaa8bcb90a9 |
children | b2e191f03473 |
files | make/autoconf/compare.sh.in make/common/Modules.gmk make/launcher/Launcher-jdk.pack.gmk make/lib/Lib-jdk.pack.gmk make/nashorn/element-list make/scripts/compare.sh make/scripts/compare_exceptions.sh.incl src/java.base/share/classes/com/sun/java/util/jar/pack/AdaptiveCoding.java src/java.base/share/classes/com/sun/java/util/jar/pack/Attribute.java src/java.base/share/classes/com/sun/java/util/jar/pack/BandStructure.java src/java.base/share/classes/com/sun/java/util/jar/pack/ClassReader.java src/java.base/share/classes/com/sun/java/util/jar/pack/ClassWriter.java src/java.base/share/classes/com/sun/java/util/jar/pack/Code.java src/java.base/share/classes/com/sun/java/util/jar/pack/Coding.java src/java.base/share/classes/com/sun/java/util/jar/pack/CodingChooser.java src/java.base/share/classes/com/sun/java/util/jar/pack/CodingMethod.java src/java.base/share/classes/com/sun/java/util/jar/pack/ConstantPool.java src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java src/java.base/share/classes/com/sun/java/util/jar/pack/Driver.java src/java.base/share/classes/com/sun/java/util/jar/pack/DriverResource.java src/java.base/share/classes/com/sun/java/util/jar/pack/DriverResource_ja.java src/java.base/share/classes/com/sun/java/util/jar/pack/DriverResource_zh_CN.java src/java.base/share/classes/com/sun/java/util/jar/pack/FixedList.java src/java.base/share/classes/com/sun/java/util/jar/pack/Fixups.java src/java.base/share/classes/com/sun/java/util/jar/pack/Histogram.java src/java.base/share/classes/com/sun/java/util/jar/pack/Instruction.java src/java.base/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java src/java.base/share/classes/com/sun/java/util/jar/pack/Package.java src/java.base/share/classes/com/sun/java/util/jar/pack/PackageReader.java src/java.base/share/classes/com/sun/java/util/jar/pack/PackageWriter.java src/java.base/share/classes/com/sun/java/util/jar/pack/PackerImpl.java src/java.base/share/classes/com/sun/java/util/jar/pack/PopulationCoding.java src/java.base/share/classes/com/sun/java/util/jar/pack/PropMap.java src/java.base/share/classes/com/sun/java/util/jar/pack/TLGlobals.java src/java.base/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java src/java.base/share/classes/com/sun/java/util/jar/pack/Utils.java src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties src/java.base/share/classes/com/sun/java/util/jar/pack/package-info.java src/java.base/share/classes/java/util/jar/Pack200.java src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java src/jdk.jartool/share/classes/sun/tools/jar/Main.java src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties src/jdk.pack/share/classes/module-info.java src/jdk.pack/share/man/pack200.1 src/jdk.pack/share/man/unpack200.1 src/jdk.pack/share/native/common-unpack/bands.cpp src/jdk.pack/share/native/common-unpack/bands.h src/jdk.pack/share/native/common-unpack/bytes.cpp src/jdk.pack/share/native/common-unpack/bytes.h src/jdk.pack/share/native/common-unpack/coding.cpp src/jdk.pack/share/native/common-unpack/coding.h src/jdk.pack/share/native/common-unpack/constants.h src/jdk.pack/share/native/common-unpack/defines.h src/jdk.pack/share/native/common-unpack/unpack.cpp src/jdk.pack/share/native/common-unpack/unpack.h src/jdk.pack/share/native/common-unpack/utils.cpp src/jdk.pack/share/native/common-unpack/utils.h src/jdk.pack/share/native/common-unpack/zip.cpp src/jdk.pack/share/native/common-unpack/zip.h src/jdk.pack/share/native/libunpack/jni.cpp src/jdk.pack/share/native/unpack200/main.cpp src/jdk.pack/windows/native/unpack200/unpack200_proto.exe.manifest src/utils/IdealGraphVisualizer/nbproject/project.properties test/jdk/ProblemList.txt test/jdk/TEST.groups test/jdk/java/util/jar/Pack200/SecurityTest.java test/jdk/tools/jar/DeprecateOptionN.java test/jdk/tools/launcher/HelpFlagsTest.java test/jdk/tools/launcher/VersionCheck.java test/jdk/tools/pack200/AttributeTests.java test/jdk/tools/pack200/BandIntegrity.java test/jdk/tools/pack200/CommandLineTests.java test/jdk/tools/pack200/DeprecatePack200.java test/jdk/tools/pack200/InstructionTests.java test/jdk/tools/pack200/ModuleAttributes.java test/jdk/tools/pack200/MultiRelease.java test/jdk/tools/pack200/Pack200Props.java test/jdk/tools/pack200/Pack200Test.java test/jdk/tools/pack200/PackChecksum.java test/jdk/tools/pack200/PackTestZip64.java test/jdk/tools/pack200/PackTestZip64Manual.java test/jdk/tools/pack200/PackageVersionTest.java test/jdk/tools/pack200/RepackTest.java test/jdk/tools/pack200/T7007157.java test/jdk/tools/pack200/TestExceptions.java test/jdk/tools/pack200/TestNormal.java test/jdk/tools/pack200/TimeStamp.java test/jdk/tools/pack200/UnpackerMemoryTest.java test/jdk/tools/pack200/Utils.java test/jdk/tools/pack200/badattr.jar test/jdk/tools/pack200/pack200-verifier/data/README test/jdk/tools/pack200/pack200-verifier/data/golden.jar test/jdk/tools/pack200/pack200-verifier/make/build.xml test/jdk/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java test/jdk/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java test/jdk/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java test/jdk/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java test/jdk/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java test/jdk/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java test/jdk/tools/pack200/pack200-verifier/src/xmlkit/TokenList.java test/jdk/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java test/jdk/tools/pack200/typeannos/Lambda.java test/jdk/tools/pack200/typeannos/Readme.txt test/jdk/tools/pack200/typeannos/TargetTypes.java test/jdk/tools/pack200/typeannos/TestTypeAnnotations.java test/jdk/tools/pack200/typeannos/TypeUseTarget.java |
diffstat | 107 files changed, 4 insertions(+), 46217 deletions(-) [+] |
line wrap: on
line diff
--- a/make/autoconf/compare.sh.in Mon Dec 09 15:28:46 2019 +0100 +++ b/make/autoconf/compare.sh.in Tue Dec 10 00:36:30 2019 +0000 @@ -67,7 +67,6 @@ export TAR="@TAR@" export TEE="@TEE@" export UNIQ="@UNIQ@" -export UNPACK200="@FIXPATH@ @BOOT_JDK@/bin/unpack200" export UNARCHIVE="@UNZIP@ -q -o" export TOPDIR="@TOPDIR@"
--- a/make/common/Modules.gmk Mon Dec 09 15:28:46 2019 +0100 +++ b/make/common/Modules.gmk Tue Dec 10 00:36:30 2019 +0000 @@ -129,7 +129,6 @@ JRE_TOOL_MODULES += \ jdk.jdwp.agent \ jdk.incubator.jpackage \ - jdk.pack \ jdk.scripting.nashorn.shell \ # @@ -170,7 +169,6 @@ jdk.naming.dns \ jdk.naming.rmi \ jdk.net \ - jdk.pack \ jdk.rmic \ jdk.scripting.nashorn \ jdk.sctp \
--- a/make/launcher/Launcher-jdk.pack.gmk Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -# -# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -include LauncherCommon.gmk - -$(eval $(call SetupBuildLauncher, pack200, \ - MAIN_MODULE := java.base, \ - MAIN_CLASS := com.sun.java.util.jar.pack.Driver, \ -)) - -################################################################################ -# The order of the object files on the link command line affects the size of the resulting -# binary (at least on linux) which causes the size to differ between old and new build. - -# Tell the compiler not to export any functions unless declared so in -# the source code. On Windows, this is the default and cannot be changed. -# On Mac, we have always exported all symbols, probably due to oversight -# and/or misunderstanding. To emulate this, don't hide any symbols -# by default. -# On AIX/xlc we need at least xlc 13.1 for the symbol hiding (see JDK-8214063) -# Also provide an override for non-conformant libraries. -ifeq ($(TOOLCHAIN_TYPE), gcc) - CXXFLAGS_JDKEXE += -fvisibility=hidden - LDFLAGS_JDKEXE += -Wl,--exclude-libs,ALL -else ifeq ($(TOOLCHAIN_TYPE), clang) - ifeq ($(call isTargetOs, macosx), false) - CXXFLAGS_JDKEXE += -fvisibility=hidden - endif -else ifeq ($(TOOLCHAIN_TYPE), solstudio) - CXXFLAGS_JDKEXE += -xldscope=hidden -endif - -UNPACKEXE_SRC := $(TOPDIR)/src/jdk.pack/share/native/common-unpack \ - $(TOPDIR)/src/jdk.pack/share/native/unpack200 -UNPACKEXE_CFLAGS := -I$(TOPDIR)/src/jdk.pack/share/native/common-unpack \ - -I$(TOPDIR)/src/java.base/share/native/libjava \ - -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjava - -ifeq ($(USE_EXTERNAL_LIBZ), true) - UNPACKEXE_CFLAGS += -DSYSTEM_ZLIB - UNPACKEXE_LIBS := -lz -else - UNPACKEXE_CFLAGS += -I$(TOPDIR)/src/java.base/share/native/libzip/zlib - UNPACKEXE_ZIPOBJS := $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/zcrc32$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/deflate$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/trees$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/zadler32$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/compress$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/zutil$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/inflate$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/infback$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/inftrees$(OBJ_SUFFIX) \ - $(SUPPORT_OUTPUTDIR)/native/java.base/libzip/inffast$(OBJ_SUFFIX) - -endif - -$(eval $(call SetupJdkExecutable, BUILD_UNPACKEXE, \ - NAME := unpack200, \ - SRC := $(UNPACKEXE_SRC), \ - TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(UNPACKEXE_CFLAGS) $(CXXFLAGS_JDKEXE) -DFULL, \ - CFLAGS_release := -DPRODUCT, \ - CFLAGS_linux := -fPIC, \ - CFLAGS_solaris := -KPIC, \ - CFLAGS_macosx := -fPIC, \ - DISABLED_WARNINGS_clang := format-nonliteral, \ - DISABLED_WARNINGS_solstudio := wunreachable, \ - LDFLAGS := $(LDFLAGS_JDKEXE) $(LDFLAGS_CXX_JDK) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := $(UNPACKEXE_LIBS) $(LIBCXX), \ - OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/unpackexe, \ - MANIFEST := $(TOPDIR)/src/jdk.pack/windows/native/unpack200/unpack200_proto.exe.manifest, \ - MANIFEST_VERSION := $(VERSION_NUMBER_FOUR_POSITIONS), \ - EXTRA_OBJECT_FILES := $(UNPACKEXE_ZIPOBJS) \ -)) - -TARGETS += $(BUILD_UNPACKEXE) - -################################################################################
--- a/make/lib/Lib-jdk.pack.gmk Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -# -# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -include LibCommon.gmk - -################################################################################ - -$(eval $(call SetupJdkLibrary, BUILD_LIBUNPACK, \ - NAME := unpack, \ - EXTRA_SRC := common-unpack, \ - TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CXXFLAGS_JDKLIB) \ - -DNO_ZLIB -DUNPACK_JNI -DFULL, \ - CFLAGS_release := -DPRODUCT, \ - EXTRA_HEADER_DIRS := $(call GetJavaHeaderDir, java.base), \ - DISABLED_WARNINGS_gcc := implicit-fallthrough, \ - DISABLED_WARNINGS_clang := format-nonliteral, \ - DISABLED_WARNINGS_solstudio := wunreachable, \ - LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LDFLAGS_windows := -map:$(SUPPORT_OUTPUTDIR)/native/$(MODULE)/unpack.map -debug, \ - LIBS_unix := -ljvm $(LIBCXX) -ljava, \ - LIBS_windows := jvm.lib $(WIN_JAVA_LIB), \ -)) - -$(BUILD_LIBUNPACK): $(call FindLib, java.base, java) - -TARGETS += $(BUILD_LIBUNPACK) - -################################################################################
--- a/make/nashorn/element-list Mon Dec 09 15:28:46 2019 +0100 +++ b/make/nashorn/element-list Tue Dec 10 00:36:30 2019 +0000 @@ -260,7 +260,6 @@ module:jdk.net jdk.net jdk.nio -module:jdk.pack module:jdk.rmic module:jdk.scripting.nashorn jdk.nashorn.api.scripting
--- a/make/scripts/compare.sh Mon Dec 09 15:28:46 2019 +0100 +++ b/make/scripts/compare.sh Tue Dec 10 00:36:30 2019 +0000 @@ -529,31 +529,6 @@ (cd $OTHER_UNZIPDIR && $JIMAGE extract $OTHER_ZIP) fi - # Find all archives inside and unzip them as well to compare the contents rather than - # the archives. pie.jar.pack.gz i app3.war is corrupt, skip it. - EXCEPTIONS="pie.jar.pack.gz jdk.pack" - for pack in $($FIND $THIS_UNZIPDIR \( -name "*.pack" -o -name "*.pack.gz" \) -a \ - ! -name pie.jar.pack.gz -a ! -name jdk.pack); do - ($UNPACK200 $pack $pack.jar) - # Filter out the unzipped archives from the diff below. - EXCEPTIONS="$EXCEPTIONS $pack $pack.jar" - done - for pack in $($FIND $OTHER_UNZIPDIR \( -name "*.pack" -o -name "*.pack.gz" \) -a \ - ! -name pie.jar.pack.gz -a ! -name jdk.pack); do - ($UNPACK200 $pack $pack.jar) - EXCEPTIONS="$EXCEPTIONS $pack $pack.jar" - done - for zip in $($FIND $THIS_UNZIPDIR -name "*.jar" -o -name "*.zip"); do - $MKDIR $zip.unzip - (cd $zip.unzip && $UNARCHIVE $zip) - EXCEPTIONS="$EXCEPTIONS $zip" - done - for zip in $($FIND $OTHER_UNZIPDIR -name "*.jar" -o -name "*.zip"); do - $MKDIR $zip.unzip - (cd $zip.unzip && $UNARCHIVE $zip) - EXCEPTIONS="$EXCEPTIONS $zip" - done - CONTENTS_DIFF_FILE=$WORK_DIR/$ZIP_FILE.diff # On solaris, there is no -q option. if [ "$OPENJDK_TARGET_OS" = "solaris" ]; then
--- a/make/scripts/compare_exceptions.sh.incl Mon Dec 09 15:28:46 2019 +0100 +++ b/make/scripts/compare_exceptions.sh.incl Tue Dec 10 00:36:30 2019 +0000 @@ -50,9 +50,7 @@ SORT_SYMBOLS=" ./lib/libfontmanager.so ./lib/libjimage.so - ./lib/libunpack.so ./lib/server/libjvm.so - ./bin/unpack200 ./hotspot/gtest/server/libjvm.so " KNOWN_DIS_DIFF="
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/AdaptiveCoding.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import static com.sun.java.util.jar.pack.Constants.*; - -/** - * Adaptive coding. - * See the section "Adaptive Encodings" in the Pack200 spec. - * @author John Rose - */ -class AdaptiveCoding implements CodingMethod { - CodingMethod headCoding; - int headLength; - CodingMethod tailCoding; - - public AdaptiveCoding(int headLength, CodingMethod headCoding, CodingMethod tailCoding) { - assert(isCodableLength(headLength)); - this.headLength = headLength; - this.headCoding = headCoding; - this.tailCoding = tailCoding; - } - - public void setHeadCoding(CodingMethod headCoding) { - this.headCoding = headCoding; - } - public void setHeadLength(int headLength) { - assert(isCodableLength(headLength)); - this.headLength = headLength; - } - public void setTailCoding(CodingMethod tailCoding) { - this.tailCoding = tailCoding; - } - - public boolean isTrivial() { - return headCoding == tailCoding; - } - - // CodingMethod methods. - public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException { - writeArray(this, out, a, start, end); - } - // writeArrayTo must be coded iteratively, not recursively: - private static void writeArray(AdaptiveCoding run, OutputStream out, int[] a, int start, int end) throws IOException { - for (;;) { - int mid = start+run.headLength; - assert(mid <= end); - run.headCoding.writeArrayTo(out, a, start, mid); - start = mid; - if (run.tailCoding instanceof AdaptiveCoding) { - run = (AdaptiveCoding) run.tailCoding; - continue; - } - break; - } - run.tailCoding.writeArrayTo(out, a, start, end); - } - - public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException { - readArray(this, in, a, start, end); - } - private static void readArray(AdaptiveCoding run, InputStream in, int[] a, int start, int end) throws IOException { - for (;;) { - int mid = start+run.headLength; - assert(mid <= end); - run.headCoding.readArrayFrom(in, a, start, mid); - start = mid; - if (run.tailCoding instanceof AdaptiveCoding) { - run = (AdaptiveCoding) run.tailCoding; - continue; - } - break; - } - run.tailCoding.readArrayFrom(in, a, start, end); - } - - public static final int KX_MIN = 0; - public static final int KX_MAX = 3; - public static final int KX_LG2BASE = 4; - public static final int KX_BASE = 16; - - public static final int KB_MIN = 0x00; - public static final int KB_MAX = 0xFF; - public static final int KB_OFFSET = 1; - public static final int KB_DEFAULT = 3; - - static int getKXOf(int K) { - for (int KX = KX_MIN; KX <= KX_MAX; KX++) { - if (((K - KB_OFFSET) & ~KB_MAX) == 0) - return KX; - K >>>= KX_LG2BASE; - } - return -1; - } - - static int getKBOf(int K) { - int KX = getKXOf(K); - if (KX < 0) return -1; - K >>>= (KX * KX_LG2BASE); - return K-1; - } - - static int decodeK(int KX, int KB) { - assert(KX_MIN <= KX && KX <= KX_MAX); - assert(KB_MIN <= KB && KB <= KB_MAX); - return (KB+KB_OFFSET) << (KX * KX_LG2BASE); - } - - static int getNextK(int K) { - if (K <= 0) return 1; // 1st K value - int KX = getKXOf(K); - if (KX < 0) return Integer.MAX_VALUE; - // This is the increment we expect to apply: - int unit = 1 << (KX * KX_LG2BASE); - int mask = KB_MAX << (KX * KX_LG2BASE); - int K1 = K + unit; - K1 &= ~(unit-1); // cut off stray low-order bits - if (((K1 - unit) & ~mask) == 0) { - assert(getKXOf(K1) == KX); - return K1; - } - if (KX == KX_MAX) return Integer.MAX_VALUE; - KX += 1; - int mask2 = KB_MAX << (KX * KX_LG2BASE); - K1 |= (mask & ~mask2); - K1 += unit; - assert(getKXOf(K1) == KX); - return K1; - } - - // Is K of the form ((KB:[0..255])+1) * 16^(KX:{0..3])? - public static boolean isCodableLength(int K) { - int KX = getKXOf(K); - if (KX < 0) return false; - int unit = 1 << (KX * KX_LG2BASE); - int mask = KB_MAX << (KX * KX_LG2BASE); - return ((K - unit) & ~mask) == 0; - } - - public byte[] getMetaCoding(Coding dflt) { - //assert(!isTrivial()); // can happen - // See the isCodableLength restriction in CodingChooser. - ByteArrayOutputStream bytes = new ByteArrayOutputStream(10); - try { - makeMetaCoding(this, dflt, bytes); - } catch (IOException ee) { - throw new RuntimeException(ee); - } - return bytes.toByteArray(); - } - private static void makeMetaCoding(AdaptiveCoding run, Coding dflt, - ByteArrayOutputStream bytes) - throws IOException { - for (;;) { - CodingMethod headCoding = run.headCoding; - int headLength = run.headLength; - CodingMethod tailCoding = run.tailCoding; - int K = headLength; - assert(isCodableLength(K)); - int ADef = (headCoding == dflt)?1:0; - int BDef = (tailCoding == dflt)?1:0; - if (ADef+BDef > 1) BDef = 0; // arbitrary choice - int ABDef = 1*ADef + 2*BDef; - assert(ABDef < 3); - int KX = getKXOf(K); - int KB = getKBOf(K); - assert(decodeK(KX, KB) == K); - int KBFlag = (KB != KB_DEFAULT)?1:0; - bytes.write(_meta_run + KX + 4*KBFlag + 8*ABDef); - if (KBFlag != 0) bytes.write(KB); - if (ADef == 0) bytes.write(headCoding.getMetaCoding(dflt)); - if (tailCoding instanceof AdaptiveCoding) { - run = (AdaptiveCoding) tailCoding; - continue; // tail call, to avoid deep stack recursion - } - if (BDef == 0) bytes.write(tailCoding.getMetaCoding(dflt)); - break; - } - } - public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) { - int op = bytes[pos++] & 0xFF; - if (op < _meta_run || op >= _meta_pop) return pos-1; // backup - AdaptiveCoding prevc = null; - for (boolean keepGoing = true; keepGoing; ) { - keepGoing = false; - assert(op >= _meta_run); - op -= _meta_run; - int KX = op % 4; - int KBFlag = (op / 4) % 2; - int ABDef = (op / 8); - assert(ABDef < 3); - int ADef = (ABDef & 1); - int BDef = (ABDef & 2); - CodingMethod[] ACode = {dflt}, BCode = {dflt}; - int KB = KB_DEFAULT; - if (KBFlag != 0) - KB = bytes[pos++] & 0xFF; - if (ADef == 0) { - pos = BandStructure.parseMetaCoding(bytes, pos, dflt, ACode); - } - if (BDef == 0 && - ((op = bytes[pos] & 0xFF) >= _meta_run) && op < _meta_pop) { - pos++; - keepGoing = true; - } else if (BDef == 0) { - pos = BandStructure.parseMetaCoding(bytes, pos, dflt, BCode); - } - AdaptiveCoding newc = new AdaptiveCoding(decodeK(KX, KB), - ACode[0], BCode[0]); - if (prevc == null) { - res[0] = newc; - } else { - prevc.tailCoding = newc; - } - prevc = newc; - } - return pos; - } - - private String keyString(CodingMethod m) { - if (m instanceof Coding) - return ((Coding)m).keyString(); - return m.toString(); - } - public String toString() { - StringBuilder res = new StringBuilder(20); - AdaptiveCoding run = this; - res.append("run("); - for (;;) { - res.append(run.headLength).append("*"); - res.append(keyString(run.headCoding)); - if (run.tailCoding instanceof AdaptiveCoding) { - run = (AdaptiveCoding) run.tailCoding; - res.append(" "); - continue; - } - break; - } - res.append(" **").append(keyString(run.tailCoding)); - res.append(")"); - return res.toString(); - } - -/* - public static void main(String av[]) { - int[][] samples = { - {1,2,3,4,5}, - {254,255,256,256+1*16,256+2*16}, - {0xfd,0xfe,0xff,0x100,0x110,0x120,0x130}, - {0xfd0,0xfe0,0xff0,0x1000,0x1100,0x1200,0x1300}, - {0xfd00,0xfe00,0xff00,0x10000,0x11000,0x12000,0x13000}, - {0xfd000,0xfe000,0xff000,0x100000} - }; - for (int i = 0; i < samples.length; i++) { - for (int j = 0; j < samples[i].length; j++) { - int K = samples[i][j]; - int KX = getKXOf(K); - int KB = getKBOf(K); - System.out.println("K="+Integer.toHexString(K)+ - " KX="+KX+" KB="+KB); - assert(isCodableLength(K)); - assert(K == decodeK(KX, KB)); - if (j == 0) continue; - int K1 = samples[i][j-1]; - assert(K == getNextK(K1)); - } - } - } -//*/ - -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/Attribute.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1699 +0,0 @@ -/* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.Index; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import static com.sun.java.util.jar.pack.Constants.*; - -/** - * Represents an attribute in a class-file. - * Takes care to remember where constant pool indexes occur. - * Implements the "little language" of Pack200 for describing - * attribute layouts. - * @author John Rose - */ -class Attribute implements Comparable<Attribute> { - // Attribute instance fields. - - Layout def; // the name and format of this attr - byte[] bytes; // the actual bytes - Object fixups; // reference relocations, if any are required - - public String name() { return def.name(); } - public Layout layout() { return def; } - public byte[] bytes() { return bytes; } - public int size() { return bytes.length; } - public Entry getNameRef() { return def.getNameRef(); } - - private Attribute(Attribute old) { - this.def = old.def; - this.bytes = old.bytes; - this.fixups = old.fixups; - } - - public Attribute(Layout def, byte[] bytes, Object fixups) { - this.def = def; - this.bytes = bytes; - this.fixups = fixups; - Fixups.setBytes(fixups, bytes); - } - public Attribute(Layout def, byte[] bytes) { - this(def, bytes, null); - } - - public Attribute addContent(byte[] bytes, Object fixups) { - assert(isCanonical()); - if (bytes.length == 0 && fixups == null) - return this; - Attribute res = new Attribute(this); - res.bytes = bytes; - res.fixups = fixups; - Fixups.setBytes(fixups, bytes); - return res; - } - public Attribute addContent(byte[] bytes) { - return addContent(bytes, null); - } - - public void finishRefs(Index ix) { - if (fixups != null) { - Fixups.finishRefs(fixups, bytes, ix); - fixups = null; - } - } - - public boolean isCanonical() { - return this == def.canon; - } - - @Override - public int compareTo(Attribute that) { - return this.def.compareTo(that.def); - } - - private static final Map<List<Attribute>, List<Attribute>> canonLists = new HashMap<>(); - private static final Map<Layout, Attribute> attributes = new HashMap<>(); - private static final Map<Layout, Attribute> standardDefs = new HashMap<>(); - - // Canonicalized lists of trivial attrs (Deprecated, etc.) - // are used by trimToSize, in order to reduce footprint - // of some common cases. (Note that Code attributes are - // always zero size.) - public static List<Attribute> getCanonList(List<Attribute> al) { - synchronized (canonLists) { - List<Attribute> cl = canonLists.get(al); - if (cl == null) { - cl = new ArrayList<>(al.size()); - cl.addAll(al); - cl = Collections.unmodifiableList(cl); - canonLists.put(al, cl); - } - return cl; - } - } - - // Find the canonical empty attribute with the given ctype, name, layout. - public static Attribute find(int ctype, String name, String layout) { - Layout key = Layout.makeKey(ctype, name, layout); - synchronized (attributes) { - Attribute a = attributes.get(key); - if (a == null) { - a = new Layout(ctype, name, layout).canonicalInstance(); - attributes.put(key, a); - } - return a; - } - } - - public static Layout keyForLookup(int ctype, String name) { - return Layout.makeKey(ctype, name); - } - - // Find canonical empty attribute with given ctype and name, - // and with the standard layout. - public static Attribute lookup(Map<Layout, Attribute> defs, int ctype, - String name) { - if (defs == null) { - defs = standardDefs; - } - return defs.get(Layout.makeKey(ctype, name)); - } - - public static Attribute define(Map<Layout, Attribute> defs, int ctype, - String name, String layout) { - Attribute a = find(ctype, name, layout); - defs.put(Layout.makeKey(ctype, name), a); - return a; - } - - static { - Map<Layout, Attribute> sd = standardDefs; - define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH"); - define(sd, ATTR_CONTEXT_CLASS, "Synthetic", ""); - define(sd, ATTR_CONTEXT_CLASS, "Deprecated", ""); - define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); - define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); - define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); - define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]"); - - define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH"); - define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); - define(sd, ATTR_CONTEXT_FIELD, "Deprecated", ""); - define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH"); - - define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH"); - define(sd, ATTR_CONTEXT_METHOD, "Synthetic", ""); - define(sd, ATTR_CONTEXT_METHOD, "Deprecated", ""); - define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]"); - define(sd, ATTR_CONTEXT_METHOD, "MethodParameters", "NB[RUNHFH]"); - //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]"); - - define(sd, ATTR_CONTEXT_CODE, "StackMapTable", - ("[NH[(1)]]" + - "[TB" + - "(64-127)[(2)]" + - "(247)[(1)(2)]" + - "(248-251)[(1)]" + - "(252)[(1)(2)]" + - "(253)[(1)(2)(2)]" + - "(254)[(1)(2)(2)(2)]" + - "(255)[(1)NH[(2)]NH[(2)]]" + - "()[]" + - "]" + - "[H]" + - "[TB(7)[RCH](8)[PH]()[]]")); - - define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]"); - define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]"); - define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable", "NH[PHOHRUHRSHH]"); - //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]"); - //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]"); - - // Note: Code and InnerClasses are special-cased elsewhere. - // Their layout specs. are given here for completeness. - // The Code spec is incomplete, in that it does not distinguish - // bytecode bytes or locate CP references. - // The BootstrapMethods attribute is also special-cased - // elsewhere as an appendix to the local constant pool. - } - - // Metadata. - // - // We define metadata using similar layouts - // for all five kinds of metadata attributes and 2 type metadata attributes - // - // Regular annotations are a counted list of [RSHNH[RUH(1)]][...] - // pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...] - // - // Parameter annotations are a counted list of regular annotations. - // pack.method.attribute.RuntimeVisibleParameterAnnotations=[NB[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...] - // - // RuntimeInvisible annotations are defined similarly... - // Non-method annotations are defined similarly... - // - // Annotation are a simple tagged value [TB...] - // pack.attribute.method.AnnotationDefault=[TB...] - - static { - String mdLayouts[] = { - Attribute.normalizeLayoutString - ("" - +"\n # parameter_annotations :=" - +"\n [ NB[(1)] ] # forward call to annotations" - ), - Attribute.normalizeLayoutString - ("" - +"\n # annotations :=" - +"\n [ NH[(1)] ] # forward call to annotation" - +"\n " - ), - Attribute.normalizeLayoutString - ("" - +"\n # annotation :=" - +"\n [RSH" - +"\n NH[RUH (1)] # forward call to value" - +"\n ]" - ), - Attribute.normalizeLayoutString - ("" - +"\n # value :=" - +"\n [TB # Callable 2 encodes one tagged value." - +"\n (\\B,\\C,\\I,\\S,\\Z)[KIH]" - +"\n (\\D)[KDH]" - +"\n (\\F)[KFH]" - +"\n (\\J)[KJH]" - +"\n (\\c)[RSH]" - +"\n (\\e)[RSH RUH]" - +"\n (\\s)[RUH]" - +"\n (\\[)[NH[(0)]] # backward self-call to value" - +"\n (\\@)[RSH NH[RUH (0)]] # backward self-call to value" - +"\n ()[] ]" - ) - }; - /* - * RuntimeVisibleTypeAnnotation and RuntimeInvisibleTypeAnnotatation are - * similar to RuntimeVisibleAnnotation and RuntimeInvisibleAnnotation, - * a type-annotation union and a type-path structure precedes the - * annotation structure - */ - String typeLayouts[] = { - Attribute.normalizeLayoutString - ("" - +"\n # type-annotations :=" - +"\n [ NH[(1)(2)(3)] ] # forward call to type-annotations" - ), - Attribute.normalizeLayoutString - ( "" - +"\n # type-annotation :=" - +"\n [TB" - +"\n (0-1) [B] # {CLASS, METHOD}_TYPE_PARAMETER" - +"\n (16) [FH] # CLASS_EXTENDS" - +"\n (17-18) [BB] # {CLASS, METHOD}_TYPE_PARAMETER_BOUND" - +"\n (19-21) [] # FIELD, METHOD_RETURN, METHOD_RECEIVER" - +"\n (22) [B] # METHOD_FORMAL_PARAMETER" - +"\n (23) [H] # THROWS" - +"\n (64-65) [NH[PHOHH]] # LOCAL_VARIABLE, RESOURCE_VARIABLE" - +"\n (66) [H] # EXCEPTION_PARAMETER" - +"\n (67-70) [PH] # INSTANCEOF, NEW, {CONSTRUCTOR, METHOD}_REFERENCE_RECEIVER" - +"\n (71-75) [PHB] # CAST, {CONSTRUCTOR,METHOD}_INVOCATION_TYPE_ARGUMENT, {CONSTRUCTOR, METHOD}_REFERENCE_TYPE_ARGUMENT" - +"\n ()[] ]" - ), - Attribute.normalizeLayoutString - ("" - +"\n # type-path" - +"\n [ NB[BB] ]" - ) - }; - Map<Layout, Attribute> sd = standardDefs; - String defaultLayout = mdLayouts[3]; - String annotationsLayout = mdLayouts[1] + mdLayouts[2] + mdLayouts[3]; - String paramsLayout = mdLayouts[0] + annotationsLayout; - String typesLayout = typeLayouts[0] + typeLayouts[1] + - typeLayouts[2] + mdLayouts[2] + mdLayouts[3]; - - for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { - if (ctype != ATTR_CONTEXT_CODE) { - define(sd, ctype, - "RuntimeVisibleAnnotations", annotationsLayout); - define(sd, ctype, - "RuntimeInvisibleAnnotations", annotationsLayout); - - if (ctype == ATTR_CONTEXT_METHOD) { - define(sd, ctype, - "RuntimeVisibleParameterAnnotations", paramsLayout); - define(sd, ctype, - "RuntimeInvisibleParameterAnnotations", paramsLayout); - define(sd, ctype, - "AnnotationDefault", defaultLayout); - } - } - define(sd, ctype, - "RuntimeVisibleTypeAnnotations", typesLayout); - define(sd, ctype, - "RuntimeInvisibleTypeAnnotations", typesLayout); - } - } - - public static String contextName(int ctype) { - switch (ctype) { - case ATTR_CONTEXT_CLASS: return "class"; - case ATTR_CONTEXT_FIELD: return "field"; - case ATTR_CONTEXT_METHOD: return "method"; - case ATTR_CONTEXT_CODE: return "code"; - } - return null; - } - - /** Base class for any attributed object (Class, Field, Method, Code). - * Flags are included because they are used to help transmit the - * presence of attributes. That is, flags are a mix of modifier - * bits and attribute indicators. - */ - public abstract static - class Holder { - - // We need this abstract method to interpret embedded CP refs. - protected abstract Entry[] getCPMap(); - - protected int flags; // defined here for convenience - protected List<Attribute> attributes; - - public int attributeSize() { - return (attributes == null) ? 0 : attributes.size(); - } - - public void trimToSize() { - if (attributes == null) { - return; - } - if (attributes.isEmpty()) { - attributes = null; - return; - } - if (attributes instanceof ArrayList) { - ArrayList<Attribute> al = (ArrayList<Attribute>)attributes; - al.trimToSize(); - boolean allCanon = true; - for (Attribute a : al) { - if (!a.isCanonical()) { - allCanon = false; - } - if (a.fixups != null) { - assert(!a.isCanonical()); - a.fixups = Fixups.trimToSize(a.fixups); - } - } - if (allCanon) { - // Replace private writable attribute list - // with only trivial entries by public unique - // immutable attribute list with the same entries. - attributes = getCanonList(al); - } - } - } - - public void addAttribute(Attribute a) { - if (attributes == null) - attributes = new ArrayList<>(3); - else if (!(attributes instanceof ArrayList)) - attributes = new ArrayList<>(attributes); // unfreeze it - attributes.add(a); - } - - public Attribute removeAttribute(Attribute a) { - if (attributes == null) return null; - if (!attributes.contains(a)) return null; - if (!(attributes instanceof ArrayList)) - attributes = new ArrayList<>(attributes); // unfreeze it - attributes.remove(a); - return a; - } - - public Attribute getAttribute(int n) { - return attributes.get(n); - } - - protected void visitRefs(int mode, Collection<Entry> refs) { - if (attributes == null) return; - for (Attribute a : attributes) { - a.visitRefs(this, mode, refs); - } - } - - static final List<Attribute> noAttributes = Arrays.asList(new Attribute[0]); - - public List<Attribute> getAttributes() { - if (attributes == null) - return noAttributes; - return attributes; - } - - public void setAttributes(List<Attribute> attrList) { - if (attrList.isEmpty()) - attributes = null; - else - attributes = attrList; - } - - public Attribute getAttribute(String attrName) { - if (attributes == null) return null; - for (Attribute a : attributes) { - if (a.name().equals(attrName)) - return a; - } - return null; - } - - public Attribute getAttribute(Layout attrDef) { - if (attributes == null) return null; - for (Attribute a : attributes) { - if (a.layout() == attrDef) - return a; - } - return null; - } - - public Attribute removeAttribute(String attrName) { - return removeAttribute(getAttribute(attrName)); - } - - public Attribute removeAttribute(Layout attrDef) { - return removeAttribute(getAttribute(attrDef)); - } - - public void strip(String attrName) { - removeAttribute(getAttribute(attrName)); - } - } - - // Lightweight interface to hide details of band structure. - // Also used for testing. - public abstract static - class ValueStream { - public int getInt(int bandIndex) { throw undef(); } - public void putInt(int bandIndex, int value) { throw undef(); } - public Entry getRef(int bandIndex) { throw undef(); } - public void putRef(int bandIndex, Entry ref) { throw undef(); } - // Note: decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref - public int decodeBCI(int bciCode) { throw undef(); } - public int encodeBCI(int bci) { throw undef(); } - public void noteBackCall(int whichCallable) { /* ignore by default */ } - private RuntimeException undef() { - return new UnsupportedOperationException("ValueStream method"); - } - } - - // Element kinds: - static final byte EK_INT = 1; // B H I SH etc. - static final byte EK_BCI = 2; // PH POH etc. - static final byte EK_BCO = 3; // OH etc. - static final byte EK_FLAG = 4; // FH etc. - static final byte EK_REPL = 5; // NH[...] etc. - static final byte EK_REF = 6; // RUH, RUNH, KQH, etc. - static final byte EK_UN = 7; // TB(...)[...] etc. - static final byte EK_CASE = 8; // (...)[...] etc. - static final byte EK_CALL = 9; // (0), (1), etc. - static final byte EK_CBLE = 10; // [...][...] etc. - static final byte EF_SIGN = 1<<0; // INT is signed - static final byte EF_DELTA = 1<<1; // BCI/BCI value is diff'ed w/ previous - static final byte EF_NULL = 1<<2; // null REF is expected/allowed - static final byte EF_BACK = 1<<3; // call, callable, case is backward - static final int NO_BAND_INDEX = -1; - - /** A "class" of attributes, characterized by a context-type, name - * and format. The formats are specified in a "little language". - */ - public static - class Layout implements Comparable<Layout> { - int ctype; // attribute context type, e.g., ATTR_CONTEXT_CODE - String name; // name of attribute - boolean hasRefs; // this kind of attr contains CP refs? - String layout; // layout specification - int bandCount; // total number of elems - Element[] elems; // tokenization of layout - Attribute canon; // canonical instance of this layout - - public int ctype() { return ctype; } - public String name() { return name; } - public String layout() { return layout; } - public Attribute canonicalInstance() { return canon; } - - public Entry getNameRef() { - return ConstantPool.getUtf8Entry(name()); - } - - public boolean isEmpty() { - return layout.isEmpty(); - } - - public Layout(int ctype, String name, String layout) { - this.ctype = ctype; - this.name = name.intern(); - this.layout = layout.intern(); - assert(ctype < ATTR_CONTEXT_LIMIT); - boolean hasCallables = layout.startsWith("["); - try { - if (!hasCallables) { - this.elems = tokenizeLayout(this, -1, layout); - } else { - String[] bodies = splitBodies(layout); - // Make the callables now, so they can be linked immediately. - Element[] lelems = new Element[bodies.length]; - this.elems = lelems; - for (int i = 0; i < lelems.length; i++) { - Element ce = this.new Element(); - ce.kind = EK_CBLE; - ce.removeBand(); - ce.bandIndex = NO_BAND_INDEX; - ce.layout = bodies[i]; - lelems[i] = ce; - } - // Next fill them in. - for (int i = 0; i < lelems.length; i++) { - Element ce = lelems[i]; - ce.body = tokenizeLayout(this, i, bodies[i]); - } - //System.out.println(Arrays.asList(elems)); - } - } catch (StringIndexOutOfBoundsException ee) { - // simplest way to catch syntax errors... - throw new RuntimeException("Bad attribute layout: "+layout, ee); - } - // Some uses do not make a fresh one for each occurrence. - // For example, if layout == "", we only need one attr to share. - canon = new Attribute(this, noBytes); - } - private Layout() {} - static Layout makeKey(int ctype, String name, String layout) { - Layout def = new Layout(); - def.ctype = ctype; - def.name = name.intern(); - def.layout = layout.intern(); - assert(ctype < ATTR_CONTEXT_LIMIT); - return def; - } - static Layout makeKey(int ctype, String name) { - return makeKey(ctype, name, ""); - } - - public Attribute addContent(byte[] bytes, Object fixups) { - return canon.addContent(bytes, fixups); - } - public Attribute addContent(byte[] bytes) { - return canon.addContent(bytes, null); - } - - @Override - public boolean equals(Object x) { - return ( x != null) && ( x.getClass() == Layout.class ) && - equals((Layout)x); - } - public boolean equals(Layout that) { - return this.name.equals(that.name) - && this.layout.equals(that.layout) - && this.ctype == that.ctype; - } - @Override - public int hashCode() { - return (((17 + name.hashCode()) - * 37 + layout.hashCode()) - * 37 + ctype); - } - @Override - public int compareTo(Layout that) { - int r; - r = this.name.compareTo(that.name); - if (r != 0) return r; - r = this.layout.compareTo(that.layout); - if (r != 0) return r; - return this.ctype - that.ctype; - } - @Override - public String toString() { - String str = contextName(ctype)+"."+name+"["+layout+"]"; - // If -ea, print out more informative strings! - assert((str = stringForDebug()) != null); - return str; - } - private String stringForDebug() { - return contextName(ctype)+"."+name+Arrays.asList(elems); - } - - public - class Element { - String layout; // spelling in the little language - byte flags; // EF_SIGN, etc. - byte kind; // EK_UINT, etc. - byte len; // scalar length of element - byte refKind; // CONSTANT_String, etc. - int bandIndex; // which band does this element govern? - int value; // extra parameter - Element[] body; // extra data (for replications, unions, calls) - - boolean flagTest(byte mask) { return (flags & mask) != 0; } - - Element() { - bandIndex = bandCount++; - } - - void removeBand() { - --bandCount; - assert(bandIndex == bandCount); - bandIndex = NO_BAND_INDEX; - } - - public boolean hasBand() { - return bandIndex >= 0; - } - public String toString() { - String str = layout; - // If -ea, print out more informative strings! - assert((str = stringForDebug()) != null); - return str; - } - private String stringForDebug() { - Element[] lbody = this.body; - switch (kind) { - case EK_CALL: - lbody = null; - break; - case EK_CASE: - if (flagTest(EF_BACK)) - lbody = null; - break; - } - return layout - + (!hasBand()?"":"#"+bandIndex) - + "<"+ (flags==0?"":""+flags)+kind+len - + (refKind==0?"":""+refKind) + ">" - + (value==0?"":"("+value+")") - + (lbody==null?"": ""+Arrays.asList(lbody)); - } - } - - public boolean hasCallables() { - return (elems.length > 0 && elems[0].kind == EK_CBLE); - } - private static final Element[] noElems = {}; - public Element[] getCallables() { - if (hasCallables()) { - Element[] nelems = Arrays.copyOf(elems, elems.length); - return nelems; - } else - return noElems; // no callables at all - } - public Element[] getEntryPoint() { - if (hasCallables()) - return elems[0].body; // body of first callable - else { - Element[] nelems = Arrays.copyOf(elems, elems.length); - return nelems; // no callables; whole body - } - } - - /** Return a sequence of tokens from the given attribute bytes. - * Sequence elements will be 1-1 correspondent with my layout tokens. - */ - public void parse(Holder holder, - byte[] bytes, int pos, int len, ValueStream out) { - int end = parseUsing(getEntryPoint(), - holder, bytes, pos, len, out); - if (end != pos + len) - throw new InternalError("layout parsed "+(end-pos)+" out of "+len+" bytes"); - } - /** Given a sequence of tokens, return the attribute bytes. - * Sequence elements must be 1-1 correspondent with my layout tokens. - * The returned object is a cookie for Fixups.finishRefs, which - * must be used to harden any references into integer indexes. - */ - public Object unparse(ValueStream in, ByteArrayOutputStream out) { - Object[] fixups = { null }; - unparseUsing(getEntryPoint(), fixups, in, out); - return fixups[0]; // return ref-bearing cookie, if any - } - - public String layoutForClassVersion(Package.Version vers) { - if (vers.lessThan(JAVA6_MAX_CLASS_VERSION)) { - // Disallow layout syntax in the oldest protocol version. - return expandCaseDashNotation(layout); - } - return layout; - } - } - - public static - class FormatException extends IOException { - @java.io.Serial - private static final long serialVersionUID = -2542243830788066513L; - - private int ctype; - private String name; - String layout; - public FormatException(String message, - int ctype, String name, String layout) { - super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" + - (message == null? "" : (": " + message))); - this.ctype = ctype; - this.name = name; - this.layout = layout; - } - public FormatException(String message, - int ctype, String name) { - this(message, ctype, name, null); - } - } - - void visitRefs(Holder holder, int mode, final Collection<Entry> refs) { - if (mode == VRM_CLASSIC) { - refs.add(getNameRef()); - } - // else the name is owned by the layout, and is processed elsewhere - if (bytes.length == 0) return; // quick exit - if (!def.hasRefs) return; // quick exit - if (fixups != null) { - Fixups.visitRefs(fixups, refs); - return; - } - // References (to a local cpMap) are embedded in the bytes. - def.parse(holder, bytes, 0, bytes.length, - new ValueStream() { - @Override - public void putInt(int bandIndex, int value) { - } - @Override - public void putRef(int bandIndex, Entry ref) { - refs.add(ref); - } - @Override - public int encodeBCI(int bci) { - return bci; - } - }); - } - - public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) { - def.parse(holder, bytes, pos, len, out); - } - public Object unparse(ValueStream in, ByteArrayOutputStream out) { - return def.unparse(in, out); - } - - @Override - public String toString() { - return def - +"{"+(bytes == null ? -1 : size())+"}" - +(fixups == null? "": fixups.toString()); - } - - /** Remove any informal "pretty printing" from the layout string. - * Removes blanks and control chars. - * Removes '#' comments (to end of line). - * Replaces '\c' by the decimal code of the character c. - * Replaces '0xNNN' by the decimal code of the hex number NNN. - */ - public static - String normalizeLayoutString(String layout) { - StringBuilder buf = new StringBuilder(); - for (int i = 0, len = layout.length(); i < len; ) { - char ch = layout.charAt(i++); - if (ch <= ' ') { - // Skip whitespace and control chars - continue; - } else if (ch == '#') { - // Skip to end of line. - int end1 = layout.indexOf('\n', i); - int end2 = layout.indexOf('\r', i); - if (end1 < 0) end1 = len; - if (end2 < 0) end2 = len; - i = Math.min(end1, end2); - } else if (ch == '\\') { - // Map a character reference to its decimal code. - buf.append((int) layout.charAt(i++)); - } else if (ch == '0' && layout.startsWith("0x", i-1)) { - // Map a hex numeral to its decimal code. - int start = i-1; - int end = start+2; - while (end < len) { - int dig = layout.charAt(end); - if ((dig >= '0' && dig <= '9') || - (dig >= 'a' && dig <= 'f')) - ++end; - else - break; - } - if (end > start) { - String num = layout.substring(start, end); - buf.append(Integer.decode(num)); - i = end; - } else { - buf.append(ch); - } - } else { - buf.append(ch); - } - } - String result = buf.toString(); - if (false && !result.equals(layout)) { - Utils.log.info("Normalizing layout string"); - Utils.log.info(" From: "+layout); - Utils.log.info(" To: "+result); - } - return result; - } - - /// Subroutines for parsing and unparsing: - - /** Parse the attribute layout language. -<pre> - attribute_layout: - ( layout_element )* | ( callable )+ - layout_element: - ( integral | replication | union | call | reference ) - - callable: - '[' body ']' - body: - ( layout_element )+ - - integral: - ( unsigned_int | signed_int | bc_index | bc_offset | flag ) - unsigned_int: - uint_type - signed_int: - 'S' uint_type - any_int: - ( unsigned_int | signed_int ) - bc_index: - ( 'P' uint_type | 'PO' uint_type ) - bc_offset: - 'O' any_int - flag: - 'F' uint_type - uint_type: - ( 'B' | 'H' | 'I' | 'V' ) - - replication: - 'N' uint_type '[' body ']' - - union: - 'T' any_int (union_case)* '(' ')' '[' (body)? ']' - union_case: - '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']' - union_case_tag: - ( numeral | numeral '-' numeral ) - call: - '(' numeral ')' - - reference: - reference_type ( 'N' )? uint_type - reference_type: - ( constant_ref | schema_ref | utf8_ref | untyped_ref ) - constant_ref: - ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' ) - schema_ref: - ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' ) - utf8_ref: - 'RU' - untyped_ref: - 'RQ' - - numeral: - '(' ('-')? (digit)+ ')' - digit: - ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) - </pre> - */ - static //private - Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) { - List<Layout.Element> col = new ArrayList<>(layout.length()); - tokenizeLayout(self, curCble, layout, col); - Layout.Element[] res = new Layout.Element[col.size()]; - col.toArray(res); - return res; - } - static //private - void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) { - boolean prevBCI = false; - for (int len = layout.length(), i = 0; i < len; ) { - int start = i; - int body; - Layout.Element e = self.new Element(); - byte kind; - //System.out.println("at "+i+": ..."+layout.substring(i)); - // strip a prefix - switch (layout.charAt(i++)) { - /// layout_element: integral - case 'B': case 'H': case 'I': case 'V': // unsigned_int - kind = EK_INT; - --i; // reparse - i = tokenizeUInt(e, layout, i); - break; - case 'S': // signed_int - kind = EK_INT; - --i; // reparse - i = tokenizeSInt(e, layout, i); - break; - case 'P': // bc_index - kind = EK_BCI; - if (layout.charAt(i++) == 'O') { - // bc_index: 'PO' tokenizeUInt - e.flags |= EF_DELTA; - // must follow P or PO: - if (!prevBCI) - { i = -i; continue; } // fail - i++; // move forward - } - --i; // reparse - i = tokenizeUInt(e, layout, i); - break; - case 'O': // bc_offset - kind = EK_BCO; - e.flags |= EF_DELTA; - // must follow P or PO: - if (!prevBCI) - { i = -i; continue; } // fail - i = tokenizeSInt(e, layout, i); - break; - case 'F': // flag - kind = EK_FLAG; - i = tokenizeUInt(e, layout, i); - break; - case 'N': // replication: 'N' uint '[' elem ... ']' - kind = EK_REPL; - i = tokenizeUInt(e, layout, i); - if (layout.charAt(i++) != '[') - { i = -i; continue; } // fail - i = skipBody(layout, body = i); - e.body = tokenizeLayout(self, curCble, - layout.substring(body, i++)); - break; - case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']' - kind = EK_UN; - i = tokenizeSInt(e, layout, i); - List<Layout.Element> cases = new ArrayList<>(); - for (;;) { - // Keep parsing cases until we hit the default case. - if (layout.charAt(i++) != '(') - { i = -i; break; } // fail - int beg = i; - i = layout.indexOf(')', i); - String cstr = layout.substring(beg, i++); - int cstrlen = cstr.length(); - if (layout.charAt(i++) != '[') - { i = -i; break; } // fail - // Check for duplication. - if (layout.charAt(i) == ']') - body = i; // missing body, which is legal here - else - i = skipBody(layout, body = i); - Layout.Element[] cbody - = tokenizeLayout(self, curCble, - layout.substring(body, i++)); - if (cstrlen == 0) { - Layout.Element ce = self.new Element(); - ce.body = cbody; - ce.kind = EK_CASE; - ce.removeBand(); - cases.add(ce); - break; // done with the whole union - } else { - // Parse a case string. - boolean firstCaseNum = true; - for (int cp = 0, endp;; cp = endp+1) { - // Look for multiple case tags: - endp = cstr.indexOf(',', cp); - if (endp < 0) endp = cstrlen; - String cstr1 = cstr.substring(cp, endp); - if (cstr1.isEmpty()) - cstr1 = "empty"; // will fail parse - int value0, value1; - // Check for a case range (new in 1.6). - int dash = findCaseDash(cstr1, 0); - if (dash >= 0) { - value0 = parseIntBefore(cstr1, dash); - value1 = parseIntAfter(cstr1, dash); - if (value0 >= value1) - { i = -i; break; } // fail - } else { - value0 = value1 = Integer.parseInt(cstr1); - } - // Add a case for each value in value0..value1 - for (;; value0++) { - Layout.Element ce = self.new Element(); - ce.body = cbody; // all cases share one body - ce.kind = EK_CASE; - ce.removeBand(); - if (!firstCaseNum) - // "backward case" repeats a body - ce.flags |= EF_BACK; - firstCaseNum = false; - ce.value = value0; - cases.add(ce); - if (value0 == value1) break; - } - if (endp == cstrlen) { - break; // done with this case - } - } - } - } - e.body = new Layout.Element[cases.size()]; - cases.toArray(e.body); - e.kind = kind; - for (int j = 0; j < e.body.length-1; j++) { - Layout.Element ce = e.body[j]; - if (matchCase(e, ce.value) != ce) { - // Duplicate tag. - { i = -i; break; } // fail - } - } - break; - case '(': // call: '(' '-'? digit+ ')' - kind = EK_CALL; - e.removeBand(); - i = layout.indexOf(')', i); - String cstr = layout.substring(start+1, i++); - int offset = Integer.parseInt(cstr); - int target = curCble + offset; - if (!(offset+"").equals(cstr) || - self.elems == null || - target < 0 || - target >= self.elems.length) - { i = -i; continue; } // fail - Layout.Element ce = self.elems[target]; - assert(ce.kind == EK_CBLE); - e.value = target; - e.body = new Layout.Element[]{ ce }; - // Is it a (recursive) backward call? - if (offset <= 0) { - // Yes. Mark both caller and callee backward. - e.flags |= EF_BACK; - ce.flags |= EF_BACK; - } - break; - case 'K': // reference_type: constant_ref - kind = EK_REF; - switch (layout.charAt(i++)) { - case 'I': e.refKind = CONSTANT_Integer; break; - case 'J': e.refKind = CONSTANT_Long; break; - case 'F': e.refKind = CONSTANT_Float; break; - case 'D': e.refKind = CONSTANT_Double; break; - case 'S': e.refKind = CONSTANT_String; break; - case 'Q': e.refKind = CONSTANT_FieldSpecific; break; - - // new in 1.7: - case 'M': e.refKind = CONSTANT_MethodHandle; break; - case 'T': e.refKind = CONSTANT_MethodType; break; - case 'L': e.refKind = CONSTANT_LoadableValue; break; - default: { i = -i; continue; } // fail - } - break; - case 'R': // schema_ref - kind = EK_REF; - switch (layout.charAt(i++)) { - case 'C': e.refKind = CONSTANT_Class; break; - case 'S': e.refKind = CONSTANT_Signature; break; - case 'D': e.refKind = CONSTANT_NameandType; break; - case 'F': e.refKind = CONSTANT_Fieldref; break; - case 'M': e.refKind = CONSTANT_Methodref; break; - case 'I': e.refKind = CONSTANT_InterfaceMethodref; break; - - case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref - case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref - - // new in 1.7: - case 'Y': e.refKind = CONSTANT_InvokeDynamic; break; - case 'B': e.refKind = CONSTANT_BootstrapMethod; break; - case 'N': e.refKind = CONSTANT_AnyMember; break; - - default: { i = -i; continue; } // fail - } - break; - default: { i = -i; continue; } // fail - } - - // further parsing of refs - if (kind == EK_REF) { - // reference: reference_type -><- ( 'N' )? tokenizeUInt - if (layout.charAt(i++) == 'N') { - e.flags |= EF_NULL; - i++; // move forward - } - --i; // reparse - i = tokenizeUInt(e, layout, i); - self.hasRefs = true; - } - - prevBCI = (kind == EK_BCI); - - // store the new element - e.kind = kind; - e.layout = layout.substring(start, i); - col.add(e); - } - } - static //private - String[] splitBodies(String layout) { - List<String> bodies = new ArrayList<>(); - // Parse several independent layout bodies: "[foo][bar]...[baz]" - for (int i = 0; i < layout.length(); i++) { - if (layout.charAt(i++) != '[') - layout.charAt(-i); // throw error - int body; - i = skipBody(layout, body = i); - bodies.add(layout.substring(body, i)); - } - String[] res = new String[bodies.size()]; - bodies.toArray(res); - return res; - } - private static - int skipBody(String layout, int i) { - assert(layout.charAt(i-1) == '['); - if (layout.charAt(i) == ']') - // No empty bodies, please. - return -i; - // skip balanced [...[...]...] - for (int depth = 1; depth > 0; ) { - switch (layout.charAt(i++)) { - case '[': depth++; break; - case ']': depth--; break; - } - } - --i; // get before bracket - assert(layout.charAt(i) == ']'); - return i; // return closing bracket - } - private static - int tokenizeUInt(Layout.Element e, String layout, int i) { - switch (layout.charAt(i++)) { - case 'V': e.len = 0; break; - case 'B': e.len = 1; break; - case 'H': e.len = 2; break; - case 'I': e.len = 4; break; - default: return -i; - } - return i; - } - private static - int tokenizeSInt(Layout.Element e, String layout, int i) { - if (layout.charAt(i) == 'S') { - e.flags |= EF_SIGN; - ++i; - } - return tokenizeUInt(e, layout, i); - } - - private static - boolean isDigit(char c) { - return c >= '0' && c <= '9'; - } - - /** Find an occurrence of hyphen '-' between two numerals. */ - static //private - int findCaseDash(String layout, int fromIndex) { - if (fromIndex <= 0) fromIndex = 1; // minimum dash pos - int lastDash = layout.length() - 2; // maximum dash pos - for (;;) { - int dash = layout.indexOf('-', fromIndex); - if (dash < 0 || dash > lastDash) return -1; - if (isDigit(layout.charAt(dash-1))) { - char afterDash = layout.charAt(dash+1); - if (afterDash == '-' && dash+2 < layout.length()) - afterDash = layout.charAt(dash+2); - if (isDigit(afterDash)) { - // matched /[0-9]--?[0-9]/; return position of dash - return dash; - } - } - fromIndex = dash+1; - } - } - static - int parseIntBefore(String layout, int dash) { - int end = dash; - int beg = end; - while (beg > 0 && isDigit(layout.charAt(beg-1))) { - --beg; - } - if (beg == end) return Integer.parseInt("empty"); - // skip backward over a sign - if (beg >= 1 && layout.charAt(beg-1) == '-') --beg; - assert(beg == 0 || !isDigit(layout.charAt(beg-1))); - return Integer.parseInt(layout.substring(beg, end)); - } - static - int parseIntAfter(String layout, int dash) { - int beg = dash+1; - int end = beg; - int limit = layout.length(); - if (end < limit && layout.charAt(end) == '-') ++end; - while (end < limit && isDigit(layout.charAt(end))) { - ++end; - } - if (beg == end) return Integer.parseInt("empty"); - return Integer.parseInt(layout.substring(beg, end)); - } - /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */ - static - String expandCaseDashNotation(String layout) { - int dash = findCaseDash(layout, 0); - if (dash < 0) return layout; // no dashes (the common case) - StringBuilder result = new StringBuilder(layout.length() * 3); - int sofar = 0; // how far have we processed the layout? - for (;;) { - // for each dash, collect everything up to the dash - result.append(layout, sofar, dash); - sofar = dash+1; // skip the dash - // then collect intermediate values - int value0 = parseIntBefore(layout, dash); - int value1 = parseIntAfter(layout, dash); - assert(value0 < value1); - result.append(","); // close off value0 numeral - for (int i = value0+1; i < value1; i++) { - result.append(i); - result.append(","); // close off i numeral - } - dash = findCaseDash(layout, sofar); - if (dash < 0) break; - } - result.append(layout, sofar, layout.length()); // collect the rest - return result.toString(); - } - static { - assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5")); - assert(expandCaseDashNotation("-2--1").equals("-2,-1")); - assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1")); - assert(expandCaseDashNotation("-1-0").equals("-1,0")); - } - - // Parse attribute bytes, putting values into bands. Returns new pos. - // Used when reading a class file (local refs resolved with local cpMap). - // Also used for ad hoc scanning. - static - int parseUsing(Layout.Element[] elems, Holder holder, - byte[] bytes, int pos, int len, ValueStream out) { - int prevBCI = 0; - int prevRBCI = 0; - int end = pos + len; - int[] buf = { 0 }; // for calls to parseInt, holds 2nd result - for (int i = 0; i < elems.length; i++) { - Layout.Element e = elems[i]; - int bandIndex = e.bandIndex; - int value; - int BCI, RBCI; - switch (e.kind) { - case EK_INT: - pos = parseInt(e, bytes, pos, buf); - value = buf[0]; - out.putInt(bandIndex, value); - break; - case EK_BCI: // PH, POH - pos = parseInt(e, bytes, pos, buf); - BCI = buf[0]; - RBCI = out.encodeBCI(BCI); - if (!e.flagTest(EF_DELTA)) { - // PH: transmit R(bci), store bci - value = RBCI; - } else { - // POH: transmit D(R(bci)), store bci - value = RBCI - prevRBCI; - } - prevBCI = BCI; - prevRBCI = RBCI; - out.putInt(bandIndex, value); - break; - case EK_BCO: // OH - assert(e.flagTest(EF_DELTA)); - // OH: transmit D(R(bci)), store D(bci) - pos = parseInt(e, bytes, pos, buf); - BCI = prevBCI + buf[0]; - RBCI = out.encodeBCI(BCI); - value = RBCI - prevRBCI; - prevBCI = BCI; - prevRBCI = RBCI; - out.putInt(bandIndex, value); - break; - case EK_FLAG: - pos = parseInt(e, bytes, pos, buf); - value = buf[0]; - out.putInt(bandIndex, value); - break; - case EK_REPL: - pos = parseInt(e, bytes, pos, buf); - value = buf[0]; - out.putInt(bandIndex, value); - for (int j = 0; j < value; j++) { - pos = parseUsing(e.body, holder, bytes, pos, end-pos, out); - } - break; // already transmitted the scalar value - case EK_UN: - pos = parseInt(e, bytes, pos, buf); - value = buf[0]; - out.putInt(bandIndex, value); - Layout.Element ce = matchCase(e, value); - pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out); - - break; // already transmitted the scalar value - case EK_CALL: - // Adjust band offset if it is a backward call. - assert(e.body.length == 1); - assert(e.body[0].kind == EK_CBLE); - if (e.flagTest(EF_BACK)) - out.noteBackCall(e.value); - pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out); - break; // no additional scalar value to transmit - case EK_REF: - pos = parseInt(e, bytes, pos, buf); - int localRef = buf[0]; - Entry globalRef; - if (localRef == 0) { - globalRef = null; // N.B. global null reference is -1 - } else { - Entry[] cpMap = holder.getCPMap(); - globalRef = (localRef >= 0 && localRef < cpMap.length - ? cpMap[localRef] - : null); - byte tag = e.refKind; - if (globalRef != null && tag == CONSTANT_Signature - && globalRef.getTag() == CONSTANT_Utf8) { - // Cf. ClassReader.readSignatureRef. - String typeName = globalRef.stringValue(); - globalRef = ConstantPool.getSignatureEntry(typeName); - } - String got = (globalRef == null - ? "invalid CP index" - : "type=" + ConstantPool.tagName(globalRef.tag)); - if (globalRef == null || !globalRef.tagMatches(tag)) { - throw new IllegalArgumentException( - "Bad constant, expected type=" + - ConstantPool.tagName(tag) + " got " + got); - } - } - out.putRef(bandIndex, globalRef); - break; - default: assert(false); - } - } - return pos; - } - - static - Layout.Element matchCase(Layout.Element e, int value) { - assert(e.kind == EK_UN); - int lastj = e.body.length-1; - for (int j = 0; j < lastj; j++) { - Layout.Element ce = e.body[j]; - assert(ce.kind == EK_CASE); - if (value == ce.value) - return ce; - } - return e.body[lastj]; - } - - private static - int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) { - int value = 0; - int loBits = e.len * 8; - // Read in big-endian order: - for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { - value += (bytes[pos++] & 0xFF) << bitPos; - } - if (loBits < 32 && e.flagTest(EF_SIGN)) { - // sign-extend subword value - int hiBits = 32 - loBits; - value = (value << hiBits) >> hiBits; - } - buf[0] = value; - return pos; - } - - // Format attribute bytes, drawing values from bands. - // Used when emptying attribute bands into a package model. - // (At that point CP refs. are not yet assigned indexes.) - static - void unparseUsing(Layout.Element[] elems, Object[] fixups, - ValueStream in, ByteArrayOutputStream out) { - int prevBCI = 0; - int prevRBCI = 0; - for (int i = 0; i < elems.length; i++) { - Layout.Element e = elems[i]; - int bandIndex = e.bandIndex; - int value; - int BCI, RBCI; // "RBCI" is R(BCI), BCI's coded representation - switch (e.kind) { - case EK_INT: - value = in.getInt(bandIndex); - unparseInt(e, value, out); - break; - case EK_BCI: // PH, POH - value = in.getInt(bandIndex); - if (!e.flagTest(EF_DELTA)) { - // PH: transmit R(bci), store bci - RBCI = value; - } else { - // POH: transmit D(R(bci)), store bci - RBCI = prevRBCI + value; - } - assert(prevBCI == in.decodeBCI(prevRBCI)); - BCI = in.decodeBCI(RBCI); - unparseInt(e, BCI, out); - prevBCI = BCI; - prevRBCI = RBCI; - break; - case EK_BCO: // OH - value = in.getInt(bandIndex); - assert(e.flagTest(EF_DELTA)); - // OH: transmit D(R(bci)), store D(bci) - assert(prevBCI == in.decodeBCI(prevRBCI)); - RBCI = prevRBCI + value; - BCI = in.decodeBCI(RBCI); - unparseInt(e, BCI - prevBCI, out); - prevBCI = BCI; - prevRBCI = RBCI; - break; - case EK_FLAG: - value = in.getInt(bandIndex); - unparseInt(e, value, out); - break; - case EK_REPL: - value = in.getInt(bandIndex); - unparseInt(e, value, out); - for (int j = 0; j < value; j++) { - unparseUsing(e.body, fixups, in, out); - } - break; - case EK_UN: - value = in.getInt(bandIndex); - unparseInt(e, value, out); - Layout.Element ce = matchCase(e, value); - unparseUsing(ce.body, fixups, in, out); - break; - case EK_CALL: - assert(e.body.length == 1); - assert(e.body[0].kind == EK_CBLE); - unparseUsing(e.body[0].body, fixups, in, out); - break; - case EK_REF: - Entry globalRef = in.getRef(bandIndex); - int localRef; - if (globalRef != null) { - // It's a one-element array, really an lvalue. - fixups[0] = Fixups.addRefWithLoc(fixups[0], out.size(), globalRef); - localRef = 0; // placeholder for fixups - } else { - localRef = 0; // fixed null value - } - unparseInt(e, localRef, out); - break; - default: assert(false); continue; - } - } - } - - private static - void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) { - int loBits = e.len * 8; - if (loBits == 0) { - // It is not stored at all ('V' layout). - return; - } - if (loBits < 32) { - int hiBits = 32 - loBits; - int codedValue; - if (e.flagTest(EF_SIGN)) - codedValue = (value << hiBits) >> hiBits; - else - codedValue = (value << hiBits) >>> hiBits; - if (codedValue != value) - throw new InternalError("cannot code in "+e.len+" bytes: "+value); - } - // Write in big-endian order: - for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { - out.write((byte)(value >>> bitPos)); - } - } - -/* - /// Testing. - public static void main(String av[]) { - int maxVal = 12; - int iters = 0; - boolean verbose; - int ap = 0; - while (ap < av.length) { - if (!av[ap].startsWith("-")) break; - if (av[ap].startsWith("-m")) - maxVal = Integer.parseInt(av[ap].substring(2)); - else if (av[ap].startsWith("-i")) - iters = Integer.parseInt(av[ap].substring(2)); - else - throw new RuntimeException("Bad option: "+av[ap]); - ap++; - } - verbose = (iters == 0); - if (iters <= 0) iters = 1; - if (ap == av.length) { - av = new String[] { - "HH", // ClassFile.version - "RUH", // SourceFile - "RCHRDNH", // EnclosingMethod - "KQH", // ConstantValue - "NH[RCH]", // Exceptions - "NH[PHH]", // LineNumberTable - "NH[PHOHRUHRSHH]", // LocalVariableTable - "NH[PHPOHIIH]", // CharacterRangeTable - "NH[PHHII]", // CoverageTable - "NH[RCHRCNHRUNHFH]", // InnerClasses - "NH[RMHNH[KLH]]", // BootstrapMethods - "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code - "=AnnotationDefault", - // Like metadata, but with a compact tag set: - "[NH[(1)]]" - +"[NH[(1)]]" - +"[RSHNH[RUH(1)]]" - +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]", - "" - }; - ap = 0; - } - Utils.currentInstance.set(new PackerImpl()); - final int[][] counts = new int[2][3]; // int bci ref - final Entry[] cpMap = new Entry[maxVal+1]; - for (int i = 0; i < cpMap.length; i++) { - if (i == 0) continue; // 0 => null - cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); - } - Package.Class cls = new Package().new Class(""); - cls.cpMap = cpMap; - class TestValueStream extends ValueStream { - java.util.Random rand = new java.util.Random(0); - ArrayList history = new ArrayList(); - int ckidx = 0; - int maxVal; - boolean verbose; - void reset() { history.clear(); ckidx = 0; } - public int getInt(int bandIndex) { - counts[0][0]++; - int value = rand.nextInt(maxVal+1); - history.add(new Integer(bandIndex)); - history.add(new Integer(value)); - return value; - } - public void putInt(int bandIndex, int token) { - counts[1][0]++; - if (verbose) - System.out.print(" "+bandIndex+":"+token); - // Make sure this put parallels a previous get: - int check0 = ((Integer)history.get(ckidx+0)).intValue(); - int check1 = ((Integer)history.get(ckidx+1)).intValue(); - if (check0 != bandIndex || check1 != token) { - if (!verbose) - System.out.println(history.subList(0, ckidx)); - System.out.println(" *** Should be "+check0+":"+check1); - throw new RuntimeException("Failed test!"); - } - ckidx += 2; - } - public Entry getRef(int bandIndex) { - counts[0][2]++; - int value = getInt(bandIndex); - if (value < 0 || value > maxVal) { - System.out.println(" *** Unexpected ref code "+value); - return ConstantPool.getLiteralEntry(new Integer(value)); - } - return cpMap[value]; - } - public void putRef(int bandIndex, Entry ref) { - counts[1][2]++; - if (ref == null) { - putInt(bandIndex, 0); - return; - } - Number refValue = null; - if (ref instanceof ConstantPool.NumberEntry) - refValue = ((ConstantPool.NumberEntry)ref).numberValue(); - int value; - if (!(refValue instanceof Integer)) { - System.out.println(" *** Unexpected ref "+ref); - value = -1; - } else { - value = ((Integer)refValue).intValue(); - } - putInt(bandIndex, value); - } - public int encodeBCI(int bci) { - counts[1][1]++; - // move LSB to MSB of low byte - int code = (bci >> 8) << 8; // keep high bits - code += (bci & 0xFE) >> 1; - code += (bci & 0x01) << 7; - return code ^ (8<<8); // mark it clearly as coded - } - public int decodeBCI(int bciCode) { - counts[0][1]++; - bciCode ^= (8<<8); // remove extra mark - int bci = (bciCode >> 8) << 8; // keep high bits - bci += (bciCode & 0x7F) << 1; - bci += (bciCode & 0x80) >> 7; - return bci; - } - } - TestValueStream tts = new TestValueStream(); - tts.maxVal = maxVal; - tts.verbose = verbose; - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - for (int i = 0; i < (1 << 30); i = (i + 1) * 5) { - int ei = tts.encodeBCI(i); - int di = tts.decodeBCI(ei); - if (di != i) System.out.println("i="+Integer.toHexString(i)+ - " ei="+Integer.toHexString(ei)+ - " di="+Integer.toHexString(di)); - } - while (iters-- > 0) { - for (int i = ap; i < av.length; i++) { - String layout = av[i]; - if (layout.startsWith("=")) { - String name = layout.substring(1); - for (Attribute a : standardDefs.values()) { - if (a.name().equals(name)) { - layout = a.layout().layout(); - break; - } - } - if (layout.startsWith("=")) { - System.out.println("Could not find "+name+" in "+standardDefs.values()); - } - } - Layout self = new Layout(0, "Foo", layout); - if (verbose) { - System.out.print("/"+layout+"/ => "); - System.out.println(Arrays.asList(self.elems)); - } - buf.reset(); - tts.reset(); - Object fixups = self.unparse(tts, buf); - byte[] bytes = buf.toByteArray(); - // Attach the references to the byte array. - Fixups.setBytes(fixups, bytes); - // Patch the references to their frozen values. - Fixups.finishRefs(fixups, bytes, new Index("test", cpMap)); - if (verbose) { - System.out.print(" bytes: {"); - for (int j = 0; j < bytes.length; j++) { - System.out.print(" "+bytes[j]); - } - System.out.println("}"); - } - if (verbose) { - System.out.print(" parse: {"); - } - self.parse(cls, bytes, 0, bytes.length, tts); - if (verbose) { - System.out.println("}"); - } - } - } - for (int j = 0; j <= 1; j++) { - System.out.print("values "+(j==0?"read":"written")+": {"); - for (int k = 0; k < counts[j].length; k++) { - System.out.print(" "+counts[j][k]); - } - System.out.println(" }"); - } - } -//*/ -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/BandStructure.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2762 +0,0 @@ -/* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.Index; -import com.sun.java.util.jar.pack.Package.Class.Field; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FilterInputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.jar.Pack200; -import static com.sun.java.util.jar.pack.Constants.*; -import java.util.LinkedList; - -/** - * Define the structure and ordering of "bands" in a packed file. - * @author John Rose - */ -@SuppressWarnings({"removal"}) -abstract -class BandStructure { - static final int MAX_EFFORT = 9; - static final int MIN_EFFORT = 1; - static final int DEFAULT_EFFORT = 5; - - // Inherit options from Pack200: - PropMap p200 = Utils.currentPropMap(); - - int verbose = p200.getInteger(Utils.DEBUG_VERBOSE); - int effort = p200.getInteger(Pack200.Packer.EFFORT); - { if (effort == 0) effort = DEFAULT_EFFORT; } - boolean optDumpBands = p200.getBoolean(Utils.COM_PREFIX+"dump.bands"); - boolean optDebugBands = p200.getBoolean(Utils.COM_PREFIX+"debug.bands"); - - // Various heuristic options. - boolean optVaryCodings = !p200.getBoolean(Utils.COM_PREFIX+"no.vary.codings"); - boolean optBigStrings = !p200.getBoolean(Utils.COM_PREFIX+"no.big.strings"); - - protected abstract Index getCPIndex(byte tag); - - // Local copy of highest class version. - private Package.Version highestClassVersion = null; - - /** Call this exactly once, early, to specify the archive major version. */ - public void initHighestClassVersion(Package.Version highestClassVersion) throws IOException { - if (this.highestClassVersion != null) { - throw new IOException( - "Highest class major version is already initialized to " + - this.highestClassVersion + "; new setting is " + highestClassVersion); - } - this.highestClassVersion = highestClassVersion; - adjustToClassVersion(); - } - - public Package.Version getHighestClassVersion() { - return highestClassVersion; - } - - private final boolean isReader = this instanceof PackageReader; - - protected BandStructure() {} - - static final Coding BYTE1 = Coding.of(1,256); - - static final Coding CHAR3 = Coding.of(3,128); - // Note: Tried sharper (3,16) with no post-zip benefit. - - // This is best used with BCI values: - static final Coding BCI5 = Coding.of(5,4); // mostly 1-byte offsets - static final Coding BRANCH5 = Coding.of(5,4,2); // mostly forward branches - - static final Coding UNSIGNED5 = Coding.of(5,64); - static final Coding UDELTA5 = UNSIGNED5.getDeltaCoding(); - // "sharp" (5,64) zips 0.4% better than "medium" (5,128) - // It zips 1.1% better than "flat" (5,192) - - static final Coding SIGNED5 = Coding.of(5,64,1); //sharp - static final Coding DELTA5 = SIGNED5.getDeltaCoding(); - // Note: Tried (5,128,2) and (5,192,2) with no benefit. - - static final Coding MDELTA5 = Coding.of(5,64,2).getDeltaCoding(); - - private static final Coding[] basicCodings = { - // Table of "Canonical BHSD Codings" from Pack200 spec. - null, // _meta_default - - // Fixed-length codings: - Coding.of(1,256,0), - Coding.of(1,256,1), - Coding.of(1,256,0).getDeltaCoding(), - Coding.of(1,256,1).getDeltaCoding(), - Coding.of(2,256,0), - Coding.of(2,256,1), - Coding.of(2,256,0).getDeltaCoding(), - Coding.of(2,256,1).getDeltaCoding(), - Coding.of(3,256,0), - Coding.of(3,256,1), - Coding.of(3,256,0).getDeltaCoding(), - Coding.of(3,256,1).getDeltaCoding(), - Coding.of(4,256,0), - Coding.of(4,256,1), - Coding.of(4,256,0).getDeltaCoding(), - Coding.of(4,256,1).getDeltaCoding(), - - // Full-range variable-length codings: - Coding.of(5, 4,0), - Coding.of(5, 4,1), - Coding.of(5, 4,2), - Coding.of(5, 16,0), - Coding.of(5, 16,1), - Coding.of(5, 16,2), - Coding.of(5, 32,0), - Coding.of(5, 32,1), - Coding.of(5, 32,2), - Coding.of(5, 64,0), - Coding.of(5, 64,1), - Coding.of(5, 64,2), - Coding.of(5,128,0), - Coding.of(5,128,1), - Coding.of(5,128,2), - - Coding.of(5, 4,0).getDeltaCoding(), - Coding.of(5, 4,1).getDeltaCoding(), - Coding.of(5, 4,2).getDeltaCoding(), - Coding.of(5, 16,0).getDeltaCoding(), - Coding.of(5, 16,1).getDeltaCoding(), - Coding.of(5, 16,2).getDeltaCoding(), - Coding.of(5, 32,0).getDeltaCoding(), - Coding.of(5, 32,1).getDeltaCoding(), - Coding.of(5, 32,2).getDeltaCoding(), - Coding.of(5, 64,0).getDeltaCoding(), - Coding.of(5, 64,1).getDeltaCoding(), - Coding.of(5, 64,2).getDeltaCoding(), - Coding.of(5,128,0).getDeltaCoding(), - Coding.of(5,128,1).getDeltaCoding(), - Coding.of(5,128,2).getDeltaCoding(), - - // Variable length subrange codings: - Coding.of(2,192,0), - Coding.of(2,224,0), - Coding.of(2,240,0), - Coding.of(2,248,0), - Coding.of(2,252,0), - - Coding.of(2, 8,0).getDeltaCoding(), - Coding.of(2, 8,1).getDeltaCoding(), - Coding.of(2, 16,0).getDeltaCoding(), - Coding.of(2, 16,1).getDeltaCoding(), - Coding.of(2, 32,0).getDeltaCoding(), - Coding.of(2, 32,1).getDeltaCoding(), - Coding.of(2, 64,0).getDeltaCoding(), - Coding.of(2, 64,1).getDeltaCoding(), - Coding.of(2,128,0).getDeltaCoding(), - Coding.of(2,128,1).getDeltaCoding(), - Coding.of(2,192,0).getDeltaCoding(), - Coding.of(2,192,1).getDeltaCoding(), - Coding.of(2,224,0).getDeltaCoding(), - Coding.of(2,224,1).getDeltaCoding(), - Coding.of(2,240,0).getDeltaCoding(), - Coding.of(2,240,1).getDeltaCoding(), - Coding.of(2,248,0).getDeltaCoding(), - Coding.of(2,248,1).getDeltaCoding(), - - Coding.of(3,192,0), - Coding.of(3,224,0), - Coding.of(3,240,0), - Coding.of(3,248,0), - Coding.of(3,252,0), - - Coding.of(3, 8,0).getDeltaCoding(), - Coding.of(3, 8,1).getDeltaCoding(), - Coding.of(3, 16,0).getDeltaCoding(), - Coding.of(3, 16,1).getDeltaCoding(), - Coding.of(3, 32,0).getDeltaCoding(), - Coding.of(3, 32,1).getDeltaCoding(), - Coding.of(3, 64,0).getDeltaCoding(), - Coding.of(3, 64,1).getDeltaCoding(), - Coding.of(3,128,0).getDeltaCoding(), - Coding.of(3,128,1).getDeltaCoding(), - Coding.of(3,192,0).getDeltaCoding(), - Coding.of(3,192,1).getDeltaCoding(), - Coding.of(3,224,0).getDeltaCoding(), - Coding.of(3,224,1).getDeltaCoding(), - Coding.of(3,240,0).getDeltaCoding(), - Coding.of(3,240,1).getDeltaCoding(), - Coding.of(3,248,0).getDeltaCoding(), - Coding.of(3,248,1).getDeltaCoding(), - - Coding.of(4,192,0), - Coding.of(4,224,0), - Coding.of(4,240,0), - Coding.of(4,248,0), - Coding.of(4,252,0), - - Coding.of(4, 8,0).getDeltaCoding(), - Coding.of(4, 8,1).getDeltaCoding(), - Coding.of(4, 16,0).getDeltaCoding(), - Coding.of(4, 16,1).getDeltaCoding(), - Coding.of(4, 32,0).getDeltaCoding(), - Coding.of(4, 32,1).getDeltaCoding(), - Coding.of(4, 64,0).getDeltaCoding(), - Coding.of(4, 64,1).getDeltaCoding(), - Coding.of(4,128,0).getDeltaCoding(), - Coding.of(4,128,1).getDeltaCoding(), - Coding.of(4,192,0).getDeltaCoding(), - Coding.of(4,192,1).getDeltaCoding(), - Coding.of(4,224,0).getDeltaCoding(), - Coding.of(4,224,1).getDeltaCoding(), - Coding.of(4,240,0).getDeltaCoding(), - Coding.of(4,240,1).getDeltaCoding(), - Coding.of(4,248,0).getDeltaCoding(), - Coding.of(4,248,1).getDeltaCoding(), - - null - }; - private static final Map<Coding, Integer> basicCodingIndexes; - static { - assert(basicCodings[_meta_default] == null); - assert(basicCodings[_meta_canon_min] != null); - assert(basicCodings[_meta_canon_max] != null); - Map<Coding, Integer> map = new HashMap<>(); - for (int i = 0; i < basicCodings.length; i++) { - Coding c = basicCodings[i]; - if (c == null) continue; - assert(i >= _meta_canon_min); - assert(i <= _meta_canon_max); - map.put(c, i); - } - basicCodingIndexes = map; - } - public static Coding codingForIndex(int i) { - return i < basicCodings.length ? basicCodings[i] : null; - } - public static int indexOf(Coding c) { - Integer i = basicCodingIndexes.get(c); - if (i == null) return 0; - return i.intValue(); - } - public static Coding[] getBasicCodings() { - return basicCodings.clone(); - } - - protected byte[] bandHeaderBytes; // used for input only - protected int bandHeaderBytePos; // BHB read pointer, for input only - protected int bandHeaderBytePos0; // for debug - - protected CodingMethod getBandHeader(int XB, Coding regularCoding) { - CodingMethod[] res = {null}; - // push back XB onto the band header bytes - bandHeaderBytes[--bandHeaderBytePos] = (byte) XB; - bandHeaderBytePos0 = bandHeaderBytePos; - // scan forward through XB and any additional band header bytes - bandHeaderBytePos = parseMetaCoding(bandHeaderBytes, - bandHeaderBytePos, - regularCoding, - res); - return res[0]; - } - - public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod[] res) { - if ((bytes[pos] & 0xFF) == _meta_default) { - res[0] = dflt; - return pos+1; - } - int pos2; - pos2 = Coding.parseMetaCoding(bytes, pos, dflt, res); - if (pos2 > pos) return pos2; - pos2 = PopulationCoding.parseMetaCoding(bytes, pos, dflt, res); - if (pos2 > pos) return pos2; - pos2 = AdaptiveCoding.parseMetaCoding(bytes, pos, dflt, res); - if (pos2 > pos) return pos2; - throw new RuntimeException("Bad meta-coding op "+(bytes[pos]&0xFF)); - } - - static final int SHORT_BAND_HEURISTIC = 100; - - public static final int NO_PHASE = 0; - - // package writing phases: - public static final int COLLECT_PHASE = 1; // collect data before write - public static final int FROZEN_PHASE = 3; // no longer collecting - public static final int WRITE_PHASE = 5; // ready to write bytes - - // package reading phases: - public static final int EXPECT_PHASE = 2; // gather expected counts - public static final int READ_PHASE = 4; // ready to read bytes - public static final int DISBURSE_PHASE = 6; // pass out data after read - - public static final int DONE_PHASE = 8; // done writing or reading - - static boolean phaseIsRead(int p) { - return (p % 2) == 0; - } - static int phaseCmp(int p0, int p1) { - assert((p0 % 2) == (p1 % 2) || (p0 % 8) == 0 || (p1 % 8) == 0); - return p0 - p1; - } - - /** The packed file is divided up into a number of segments. - * Most segments are typed as ValueBand, strongly-typed sequences - * of integer values, all interpreted in a single way. - * A few segments are ByteBands, which hetergeneous sequences - * of bytes. - * - * The two phases for writing a packed file are COLLECT and WRITE. - * 1. When writing a packed file, each band collects - * data in an ad-hoc order. - * 2. At the end, each band is assigned a coding scheme, - * and then all the bands are written in their global order. - * - * The three phases for reading a packed file are EXPECT, READ, - * and DISBURSE. - * 1. For each band, the expected number of integers is determined. - * 2. The data is actually read from the file into the band. - * 3. The band pays out its values as requested, in an ad hoc order. - * - * When the last phase of a band is done, it is marked so (DONE). - * Clearly, these phases must be properly ordered WRT each other. - */ - abstract class Band { - private int phase = NO_PHASE; - private final String name; - - private int valuesExpected; - - protected long outputSize = -1; // cache - - public final Coding regularCoding; - - public final int seqForDebug; - public int elementCountForDebug; - - - protected Band(String name, Coding regularCoding) { - this.name = name; - this.regularCoding = regularCoding; - this.seqForDebug = ++nextSeqForDebug; - if (verbose > 2) - Utils.log.fine("Band "+seqForDebug+" is "+name); - // caller must call init - } - - public Band init() { - // Cannot due this from the constructor, because constructor - // may wish to initialize some subclass variables. - // Set initial phase for reading or writing: - if (isReader) - readyToExpect(); - else - readyToCollect(); - return this; - } - - // common operations - boolean isReader() { return isReader; } - int phase() { return phase; } - String name() { return name; } - - /** Return -1 if data buffer not allocated, else max length. */ - public abstract int capacity(); - - /** Allocate data buffer to specified length. */ - protected abstract void setCapacity(int cap); - - /** Return current number of values in buffer, which must exist. */ - public abstract int length(); - - protected abstract int valuesRemainingForDebug(); - - public final int valuesExpected() { - return valuesExpected; - } - - /** Write out bytes, encoding the values. */ - public final void writeTo(OutputStream out) throws IOException { - assert(assertReadyToWriteTo(this, out)); - setPhase(WRITE_PHASE); - // subclasses continue by writing their contents to output - writeDataTo(out); - doneWriting(); - } - - abstract void chooseBandCodings() throws IOException; - - public final long outputSize() { - if (outputSize >= 0) { - long size = outputSize; - assert(size == computeOutputSize()); - return size; - } - return computeOutputSize(); - } - - protected abstract long computeOutputSize(); - - protected abstract void writeDataTo(OutputStream out) throws IOException; - - /** Expect a certain number of values. */ - void expectLength(int l) { - assert(assertPhase(this, EXPECT_PHASE)); - assert(valuesExpected == 0); // all at once - assert(l >= 0); - valuesExpected = l; - } - /** Expect more values. (Multiple calls accumulate.) */ - void expectMoreLength(int l) { - assert(assertPhase(this, EXPECT_PHASE)); - valuesExpected += l; - } - - - /// Phase change markers. - - private void readyToCollect() { // called implicitly by constructor - setCapacity(1); - setPhase(COLLECT_PHASE); - } - protected void doneWriting() { - assert(assertPhase(this, WRITE_PHASE)); - setPhase(DONE_PHASE); - } - private void readyToExpect() { // called implicitly by constructor - setPhase(EXPECT_PHASE); - } - /** Read in bytes, decoding the values. */ - public final void readFrom(InputStream in) throws IOException { - assert(assertReadyToReadFrom(this, in)); - setCapacity(valuesExpected()); - setPhase(READ_PHASE); - // subclasses continue by reading their contents from input: - readDataFrom(in); - readyToDisburse(); - } - protected abstract void readDataFrom(InputStream in) throws IOException; - protected void readyToDisburse() { - if (verbose > 1) Utils.log.fine("readyToDisburse "+this); - setPhase(DISBURSE_PHASE); - } - public void doneDisbursing() { - assert(assertPhase(this, DISBURSE_PHASE)); - setPhase(DONE_PHASE); - } - public final void doneWithUnusedBand() { - if (isReader) { - assert(assertPhase(this, EXPECT_PHASE)); - assert(valuesExpected() == 0); - // Fast forward: - setPhase(READ_PHASE); - setPhase(DISBURSE_PHASE); - setPhase(DONE_PHASE); - } else { - setPhase(FROZEN_PHASE); - } - } - - protected void setPhase(int newPhase) { - assert(assertPhaseChangeOK(this, phase, newPhase)); - this.phase = newPhase; - } - - protected int lengthForDebug = -1; // DEBUG ONLY - @Override - public String toString() { // DEBUG ONLY - int length = (lengthForDebug != -1 ? lengthForDebug : length()); - String str = name; - if (length != 0) - str += "[" + length + "]"; - if (elementCountForDebug != 0) - str += "(" + elementCountForDebug + ")"; - return str; - } - } - - class ValueBand extends Band { - private int[] values; // must be null in EXPECT phase - private int length; - private int valuesDisbursed; - - private CodingMethod bandCoding; - private byte[] metaCoding; - - protected ValueBand(String name, Coding regularCoding) { - super(name, regularCoding); - } - - @Override - public int capacity() { - return values == null ? -1 : values.length; - } - - /** Declare predicted or needed capacity. */ - @Override - protected void setCapacity(int cap) { - assert(length <= cap); - if (cap == -1) { values = null; return; } - values = realloc(values, cap); - } - - @Override - public int length() { - return length; - } - @Override - protected int valuesRemainingForDebug() { - return length - valuesDisbursed; - } - protected int valueAtForDebug(int i) { - return values[i]; - } - - void patchValue(int i, int value) { - // Only one use for this. - assert(this == archive_header_S); - assert(i == AH_ARCHIVE_SIZE_HI || i == AH_ARCHIVE_SIZE_LO); - assert(i < length); // must have already output a dummy - values[i] = value; - outputSize = -1; // decache - } - - protected void initializeValues(int[] values) { - assert(assertCanChangeLength(this)); - assert(length == 0); - this.values = values; - this.length = values.length; - } - - /** Collect one value, or store one decoded value. */ - protected void addValue(int x) { - assert(assertCanChangeLength(this)); - if (length == values.length) - setCapacity(length < 1000 ? length * 10 : length * 2); - values[length++] = x; - } - - private boolean canVaryCoding() { - if (!optVaryCodings) return false; - if (length == 0) return false; - // Can't read band_headers w/o the archive header: - if (this == archive_header_0) return false; - if (this == archive_header_S) return false; - if (this == archive_header_1) return false; - // BYTE1 bands can't vary codings, but the others can. - // All that's needed for the initial escape is at least - // 256 negative values or more than 256 non-negative values - return (regularCoding.min() <= -256 || regularCoding.max() >= 256); - } - - private boolean shouldVaryCoding() { - assert(canVaryCoding()); - if (effort < MAX_EFFORT && length < SHORT_BAND_HEURISTIC) - return false; - return true; - } - - @Override - protected void chooseBandCodings() throws IOException { - boolean canVary = canVaryCoding(); - if (!canVary || !shouldVaryCoding()) { - if (regularCoding.canRepresent(values, 0, length)) { - bandCoding = regularCoding; - } else { - assert(canVary); - if (verbose > 1) - Utils.log.fine("regular coding fails in band "+name()); - bandCoding = UNSIGNED5; - } - outputSize = -1; - } else { - int[] sizes = {0,0}; - bandCoding = chooseCoding(values, 0, length, - regularCoding, name(), - sizes); - outputSize = sizes[CodingChooser.BYTE_SIZE]; - if (outputSize == 0) // CodingChooser failed to size it. - outputSize = -1; - } - - // Compute and save the meta-coding bytes also. - if (bandCoding != regularCoding) { - metaCoding = bandCoding.getMetaCoding(regularCoding); - if (verbose > 1) { - Utils.log.fine("alternate coding "+this+" "+bandCoding); - } - } else if (canVary && - decodeEscapeValue(values[0], regularCoding) >= 0) { - // Need an explicit default. - metaCoding = defaultMetaCoding; - } else { - // Common case: Zero bytes of meta coding. - metaCoding = noMetaCoding; - } - if (metaCoding.length > 0 - && (verbose > 2 || verbose > 1 && metaCoding.length > 1)) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < metaCoding.length; i++) { - if (i == 1) sb.append(" /"); - sb.append(" ").append(metaCoding[i] & 0xFF); - } - Utils.log.fine(" meta-coding "+sb); - } - - assert((outputSize < 0) || - !(bandCoding instanceof Coding) || - (outputSize == ((Coding)bandCoding) - .getLength(values, 0, length))) - : (bandCoding+" : "+ - outputSize+" != "+ - ((Coding)bandCoding).getLength(values, 0, length) - +" ?= "+getCodingChooser().computeByteSize(bandCoding,values,0,length) - ); - - // Compute outputSize of the escape value X, if any. - if (metaCoding.length > 0) { - // First byte XB of meta-coding is treated specially, - // but any other bytes go into the band headers band. - // This must be done before any other output happens. - if (outputSize >= 0) - outputSize += computeEscapeSize(); // good cache - // Other bytes go into band_headers. - for (int i = 1; i < metaCoding.length; i++) { - band_headers.putByte(metaCoding[i] & 0xFF); - } - } - } - - @Override - protected long computeOutputSize() { - outputSize = getCodingChooser().computeByteSize(bandCoding, - values, 0, length); - assert(outputSize < Integer.MAX_VALUE); - outputSize += computeEscapeSize(); - return outputSize; - } - - protected int computeEscapeSize() { - if (metaCoding.length == 0) return 0; - int XB = metaCoding[0] & 0xFF; - int X = encodeEscapeValue(XB, regularCoding); - return regularCoding.setD(0).getLength(X); - } - - @Override - protected void writeDataTo(OutputStream out) throws IOException { - if (length == 0) return; // nothing to write - long len0 = 0; - if (out == outputCounter) { - len0 = outputCounter.getCount(); - } - if (metaCoding.length > 0) { - int XB = metaCoding[0] & 0xFF; - // We need an explicit band header, either because - // there is a non-default coding method, or because - // the first value would be parsed as an escape value. - int X = encodeEscapeValue(XB, regularCoding); - //System.out.println("X="+X+" XB="+XB+" in "+this); - regularCoding.setD(0).writeTo(out, X); - } - bandCoding.writeArrayTo(out, values, 0, length); - if (out == outputCounter) { - assert(outputSize == outputCounter.getCount() - len0) - : (outputSize+" != "+outputCounter.getCount()+"-"+len0); - } - if (optDumpBands) dumpBand(); - } - - @Override - protected void readDataFrom(InputStream in) throws IOException { - length = valuesExpected(); - if (length == 0) return; // nothing to read - if (verbose > 1) - Utils.log.fine("Reading band "+this); - if (!canVaryCoding()) { - bandCoding = regularCoding; - metaCoding = noMetaCoding; - } else { - assert(in.markSupported()); // input must be buffered - in.mark(Coding.B_MAX); - int X = regularCoding.setD(0).readFrom(in); - int XB = decodeEscapeValue(X, regularCoding); - if (XB < 0) { - // Do not consume this value. No alternate coding. - in.reset(); - bandCoding = regularCoding; - metaCoding = noMetaCoding; - } else if (XB == _meta_default) { - bandCoding = regularCoding; - metaCoding = defaultMetaCoding; - } else { - if (verbose > 2) - Utils.log.fine("found X="+X+" => XB="+XB); - bandCoding = getBandHeader(XB, regularCoding); - // This is really used only by dumpBands. - int p0 = bandHeaderBytePos0; - int p1 = bandHeaderBytePos; - metaCoding = new byte[p1-p0]; - System.arraycopy(bandHeaderBytes, p0, - metaCoding, 0, metaCoding.length); - } - } - if (bandCoding != regularCoding) { - if (verbose > 1) - Utils.log.fine(name()+": irregular coding "+bandCoding); - } - bandCoding.readArrayFrom(in, values, 0, length); - if (optDumpBands) dumpBand(); - } - - @Override - public void doneDisbursing() { - super.doneDisbursing(); - values = null; // for GC - } - - private void dumpBand() throws IOException { - assert(optDumpBands); - try (PrintStream ps = new PrintStream(getDumpStream(this, ".txt"))) { - String irr = (bandCoding == regularCoding) ? "" : " irregular"; - ps.print("# length="+length+ - " size="+outputSize()+ - irr+" coding="+bandCoding); - if (metaCoding != noMetaCoding) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < metaCoding.length; i++) { - if (i == 1) sb.append(" /"); - sb.append(" ").append(metaCoding[i] & 0xFF); - } - ps.print(" //header: "+sb); - } - printArrayTo(ps, values, 0, length); - } - try (OutputStream ds = getDumpStream(this, ".bnd")) { - bandCoding.writeArrayTo(ds, values, 0, length); - } - } - - /** Disburse one value. */ - protected int getValue() { - assert(phase() == DISBURSE_PHASE); - // when debugging return a zero if lengths are zero - if (optDebugBands && length == 0 && valuesDisbursed == length) - return 0; - assert(valuesDisbursed <= length); - return values[valuesDisbursed++]; - } - - /** Reset for another pass over the same value set. */ - public void resetForSecondPass() { - assert(phase() == DISBURSE_PHASE); - assert(valuesDisbursed == length()); // 1st pass is complete - valuesDisbursed = 0; - } - } - - class ByteBand extends Band { - private ByteArrayOutputStream bytes; // input buffer - private ByteArrayOutputStream bytesForDump; - private InputStream in; - - public ByteBand(String name) { - super(name, BYTE1); - } - - @Override - public int capacity() { - return bytes == null ? -1 : Integer.MAX_VALUE; - } - @Override - protected void setCapacity(int cap) { - assert(bytes == null); // do this just once - bytes = new ByteArrayOutputStream(cap); - } - public void destroy() { - lengthForDebug = length(); - bytes = null; - } - - @Override - public int length() { - return bytes == null ? -1 : bytes.size(); - } - public void reset() { - bytes.reset(); - } - @Override - protected int valuesRemainingForDebug() { - return (bytes == null) ? -1 : ((ByteArrayInputStream)in).available(); - } - - @Override - protected void chooseBandCodings() throws IOException { - // No-op. - assert(decodeEscapeValue(regularCoding.min(), regularCoding) < 0); - assert(decodeEscapeValue(regularCoding.max(), regularCoding) < 0); - } - - @Override - protected long computeOutputSize() { - // do not cache - return bytes.size(); - } - - @Override - public void writeDataTo(OutputStream out) throws IOException { - if (length() == 0) return; - bytes.writeTo(out); - if (optDumpBands) dumpBand(); - destroy(); // done with the bits! - } - - private void dumpBand() throws IOException { - assert(optDumpBands); - try (OutputStream ds = getDumpStream(this, ".bnd")) { - if (bytesForDump != null) - bytesForDump.writeTo(ds); - else - bytes.writeTo(ds); - } - } - - @Override - public void readDataFrom(InputStream in) throws IOException { - int vex = valuesExpected(); - if (vex == 0) return; - if (verbose > 1) { - lengthForDebug = vex; - Utils.log.fine("Reading band "+this); - lengthForDebug = -1; - } - byte[] buf = new byte[Math.min(vex, 1<<14)]; - while (vex > 0) { - int nr = in.read(buf, 0, Math.min(vex, buf.length)); - if (nr < 0) throw new EOFException(); - bytes.write(buf, 0, nr); - vex -= nr; - } - if (optDumpBands) dumpBand(); - } - - @Override - public void readyToDisburse() { - in = new ByteArrayInputStream(bytes.toByteArray()); - super.readyToDisburse(); - } - - @Override - public void doneDisbursing() { - super.doneDisbursing(); - if (optDumpBands - && bytesForDump != null && bytesForDump.size() > 0) { - try { - dumpBand(); - } catch (IOException ee) { - throw new RuntimeException(ee); - } - } - in = null; // GC - bytes = null; // GC - bytesForDump = null; // GC - } - - // alternative to readFrom: - public void setInputStreamFrom(InputStream in) throws IOException { - assert(bytes == null); - assert(assertReadyToReadFrom(this, in)); - setPhase(READ_PHASE); - this.in = in; - if (optDumpBands) { - // Tap the stream. - bytesForDump = new ByteArrayOutputStream(); - this.in = new FilterInputStream(in) { - @Override - public int read() throws IOException { - int ch = in.read(); - if (ch >= 0) bytesForDump.write(ch); - return ch; - } - @Override - public int read(byte b[], int off, int len) throws IOException { - int nr = in.read(b, off, len); - if (nr >= 0) bytesForDump.write(b, off, nr); - return nr; - } - }; - } - super.readyToDisburse(); - } - - public OutputStream collectorStream() { - assert(phase() == COLLECT_PHASE); - assert(bytes != null); - return bytes; - } - - public InputStream getInputStream() { - assert(phase() == DISBURSE_PHASE); - assert(in != null); - return in; - } - public int getByte() throws IOException { - int b = getInputStream().read(); - if (b < 0) throw new EOFException(); - return b; - } - public void putByte(int b) throws IOException { - assert(b == (b & 0xFF)); - collectorStream().write(b); - } - @Override - public String toString() { - return "byte "+super.toString(); - } - } - - class IntBand extends ValueBand { - // The usual coding for bands is 7bit/5byte/delta. - public IntBand(String name, Coding regularCoding) { - super(name, regularCoding); - } - - public void putInt(int x) { - assert(phase() == COLLECT_PHASE); - addValue(x); - } - - public int getInt() { - return getValue(); - } - /** Return the sum of all values in this band. */ - public int getIntTotal() { - assert(phase() == DISBURSE_PHASE); - // assert that this is the whole pass; no other reads allowed - assert(valuesRemainingForDebug() == length()); - int total = 0; - for (int k = length(); k > 0; k--) { - total += getInt(); - } - resetForSecondPass(); - return total; - } - /** Return the occurrence count of a specific value in this band. */ - public int getIntCount(int value) { - assert(phase() == DISBURSE_PHASE); - // assert that this is the whole pass; no other reads allowed - assert(valuesRemainingForDebug() == length()); - int total = 0; - for (int k = length(); k > 0; k--) { - if (getInt() == value) { - total += 1; - } - } - resetForSecondPass(); - return total; - } - } - - static int getIntTotal(int[] values) { - int total = 0; - for (int i = 0; i < values.length; i++) { - total += values[i]; - } - return total; - } - - class CPRefBand extends ValueBand { - Index index; - boolean nullOK; - - public CPRefBand(String name, Coding regularCoding, byte cpTag, boolean nullOK) { - super(name, regularCoding); - this.nullOK = nullOK; - if (cpTag != CONSTANT_None) - setBandIndex(this, cpTag); - } - public CPRefBand(String name, Coding regularCoding, byte cpTag) { - this(name, regularCoding, cpTag, false); - } - public CPRefBand(String name, Coding regularCoding, Object undef) { - this(name, regularCoding, CONSTANT_None, false); - } - - public void setIndex(Index index) { - this.index = index; - } - - protected void readDataFrom(InputStream in) throws IOException { - super.readDataFrom(in); - assert(assertValidCPRefs(this)); - } - - /** Write a constant pool reference. */ - public void putRef(Entry e) { - addValue(encodeRefOrNull(e, index)); - } - public void putRef(Entry e, Index index) { - assert(this.index == null); - addValue(encodeRefOrNull(e, index)); - } - public void putRef(Entry e, byte cptag) { - putRef(e, getCPIndex(cptag)); - } - - public Entry getRef() { - if (index == null) Utils.log.warning("No index for "+this); - assert(index != null); - return decodeRefOrNull(getValue(), index); - } - public Entry getRef(Index index) { - assert(this.index == null); - return decodeRefOrNull(getValue(), index); - } - public Entry getRef(byte cptag) { - return getRef(getCPIndex(cptag)); - } - - private int encodeRefOrNull(Entry e, Index index) { - int nonNullCode; // NNC is the coding which assumes nulls are rare - if (e == null) { - nonNullCode = -1; // negative values are rare - } else { - nonNullCode = encodeRef(e, index); - } - // If nulls are expected, increment, to make -1 code turn to 0. - return (nullOK ? 1 : 0) + nonNullCode; - } - private Entry decodeRefOrNull(int code, Index index) { - // Inverse to encodeRefOrNull... - int nonNullCode = code - (nullOK ? 1 : 0); - if (nonNullCode == -1) { - return null; - } else { - return decodeRef(nonNullCode, index); - } - } - } - - // Bootstrap support for CPRefBands. These are needed to record - // intended CP indexes, before the CP has been created. - private final List<CPRefBand> allKQBands = new ArrayList<>(); - private List<Object[]> needPredefIndex = new ArrayList<>(); - - - int encodeRef(Entry e, Index ix) { - if (ix == null) - throw new RuntimeException("null index for " + e.stringValue()); - int coding = ix.indexOf(e); - if (verbose > 2) - Utils.log.fine("putRef "+coding+" => "+e); - return coding; - } - - Entry decodeRef(int n, Index ix) { - if (n < 0 || n >= ix.size()) - Utils.log.warning("decoding bad ref "+n+" in "+ix); - Entry e = ix.getEntry(n); - if (verbose > 2) - Utils.log.fine("getRef "+n+" => "+e); - return e; - } - - private CodingChooser codingChooser; - protected CodingChooser getCodingChooser() { - if (codingChooser == null) { - codingChooser = new CodingChooser(effort, basicCodings); - if (codingChooser.stress != null - && this instanceof PackageWriter) { - // Twist the random state based on my first file. - // This sends each segment off in a different direction. - List<Package.Class> classes = ((PackageWriter)this).pkg.classes; - if (!classes.isEmpty()) { - Package.Class cls = classes.get(0); - codingChooser.addStressSeed(cls.getName().hashCode()); - } - } - } - return codingChooser; - } - - public CodingMethod chooseCoding(int[] values, int start, int end, - Coding regular, String bandName, - int[] sizes) { - assert(optVaryCodings); - if (effort <= MIN_EFFORT) { - return regular; - } - CodingChooser cc = getCodingChooser(); - if (verbose > 1 || cc.verbose > 1) { - Utils.log.fine("--- chooseCoding "+bandName); - } - return cc.choose(values, start, end, regular, sizes); - } - - static final byte[] defaultMetaCoding = { _meta_default }; - static final byte[] noMetaCoding = {}; - - // The first value in a band is always coded with the default coding D. - // If this first value X is an escape value, it actually represents the - // first (and perhaps only) byte of a meta-coding. - // - // If D.S != 0 and D includes the range [-256..-1], - // the escape values are in that range, - // and the first byte XB is -1-X. - // - // If D.S == 0 and D includes the range [(D.L)..(D.L)+255], - // the escape values are in that range, - // and XB is X-(D.L). - // - // This representation is designed so that a band header is unlikely - // to be confused with the initial value of a headerless band, - // and yet so that a band header is likely to occupy only a byte or two. - // - // Result is in [0..255] if XB was successfully extracted, else -1. - // See section "Coding Specifier Meta-Encoding" in the JSR 200 spec. - protected static int decodeEscapeValue(int X, Coding regularCoding) { - // The first value in a band is always coded with the default coding D. - // If this first value X is an escape value, it actually represents the - // first (and perhaps only) byte of a meta-coding. - // Result is in [0..255] if XB was successfully extracted, else -1. - if (regularCoding.B() == 1 || regularCoding.L() == 0) - return -1; // degenerate regular coding (BYTE1) - if (regularCoding.S() != 0) { - if (-256 <= X && X <= -1 && regularCoding.min() <= -256) { - int XB = -1-X; - assert(XB >= 0 && XB < 256); - return XB; - } - } else { - int L = regularCoding.L(); - if (L <= X && X <= L+255 && regularCoding.max() >= L+255) { - int XB = X-L; - assert(XB >= 0 && XB < 256); - return XB; - } - } - return -1; // negative value for failure - } - // Inverse to decodeEscapeValue(). - protected static int encodeEscapeValue(int XB, Coding regularCoding) { - assert(XB >= 0 && XB < 256); - assert(regularCoding.B() > 1 && regularCoding.L() > 0); - int X; - if (regularCoding.S() != 0) { - assert(regularCoding.min() <= -256); - X = -1-XB; - } else { - int L = regularCoding.L(); - assert(regularCoding.max() >= L+255); - X = XB+L; - } - assert(decodeEscapeValue(X, regularCoding) == XB) - : (regularCoding+" XB="+XB+" X="+X); - return X; - } - - static { - boolean checkXB = false; - assert(checkXB = true); - if (checkXB) { - for (int i = 0; i < basicCodings.length; i++) { - Coding D = basicCodings[i]; - if (D == null) continue; - if (D.B() == 1) continue; - if (D.L() == 0) continue; - for (int XB = 0; XB <= 255; XB++) { - // The following exercises decodeEscapeValue also: - encodeEscapeValue(XB, D); - } - } - } - } - - class MultiBand extends Band { - MultiBand(String name, Coding regularCoding) { - super(name, regularCoding); - } - - @Override - public Band init() { - super.init(); - // This is all just to keep the asserts happy: - setCapacity(0); - if (phase() == EXPECT_PHASE) { - // Fast forward: - setPhase(READ_PHASE); - setPhase(DISBURSE_PHASE); - } - return this; - } - - Band[] bands = new Band[10]; - int bandCount = 0; - - int size() { - return bandCount; - } - Band get(int i) { - assert(i < bandCount); - return bands[i]; - } - Band[] toArray() { - return (Band[]) realloc(bands, bandCount); - } - - void add(Band b) { - assert(bandCount == 0 || notePrevForAssert(b, bands[bandCount-1])); - if (bandCount == bands.length) { - bands = (Band[]) realloc(bands); - } - bands[bandCount++] = b; - } - - ByteBand newByteBand(String name) { - ByteBand b = new ByteBand(name); - b.init(); add(b); - return b; - } - IntBand newIntBand(String name) { - IntBand b = new IntBand(name, regularCoding); - b.init(); add(b); - return b; - } - IntBand newIntBand(String name, Coding regularCoding) { - IntBand b = new IntBand(name, regularCoding); - b.init(); add(b); - return b; - } - MultiBand newMultiBand(String name, Coding regularCoding) { - MultiBand b = new MultiBand(name, regularCoding); - b.init(); add(b); - return b; - } - CPRefBand newCPRefBand(String name, byte cpTag) { - CPRefBand b = new CPRefBand(name, regularCoding, cpTag); - b.init(); add(b); - return b; - } - CPRefBand newCPRefBand(String name, Coding regularCoding, - byte cpTag) { - CPRefBand b = new CPRefBand(name, regularCoding, cpTag); - b.init(); add(b); - return b; - } - CPRefBand newCPRefBand(String name, Coding regularCoding, - byte cpTag, boolean nullOK) { - CPRefBand b = new CPRefBand(name, regularCoding, cpTag, nullOK); - b.init(); add(b); - return b; - } - - int bandCount() { return bandCount; } - - private int cap = -1; - @Override - public int capacity() { return cap; } - @Override - public void setCapacity(int cap) { this.cap = cap; } - - @Override - public int length() { return 0; } - @Override - public int valuesRemainingForDebug() { return 0; } - - @Override - protected void chooseBandCodings() throws IOException { - // coding decision pass - for (int i = 0; i < bandCount; i++) { - Band b = bands[i]; - b.chooseBandCodings(); - } - } - - @Override - protected long computeOutputSize() { - // coding decision pass - long sum = 0; - for (int i = 0; i < bandCount; i++) { - Band b = bands[i]; - long bsize = b.outputSize(); - assert(bsize >= 0) : b; - sum += bsize; - } - // do not cache - return sum; - } - - @Override - protected void writeDataTo(OutputStream out) throws IOException { - long preCount = 0; - if (outputCounter != null) preCount = outputCounter.getCount(); - for (int i = 0; i < bandCount; i++) { - Band b = bands[i]; - b.writeTo(out); - if (outputCounter != null) { - long postCount = outputCounter.getCount(); - long len = postCount - preCount; - preCount = postCount; - if ((verbose > 0 && len > 0) || verbose > 1) { - Utils.log.info(" ...wrote "+len+" bytes from "+b); - } - } - } - } - - @Override - protected void readDataFrom(InputStream in) throws IOException { - assert(false); // not called? - for (int i = 0; i < bandCount; i++) { - Band b = bands[i]; - b.readFrom(in); - if ((verbose > 0 && b.length() > 0) || verbose > 1) { - Utils.log.info(" ...read "+b); - } - } - } - - @Override - public String toString() { - return "{"+bandCount()+" bands: "+super.toString()+"}"; - } - } - - /** - * An output stream which counts the number of bytes written. - */ - private static - class ByteCounter extends FilterOutputStream { - // (should go public under the name CountingOutputStream?) - - private long count; - - public ByteCounter(OutputStream out) { - super(out); - } - - public long getCount() { return count; } - public void setCount(long c) { count = c; } - - @Override - public void write(int b) throws IOException { - count++; - if (out != null) out.write(b); - } - @Override - public void write(byte b[], int off, int len) throws IOException { - count += len; - if (out != null) out.write(b, off, len); - } - @Override - public String toString() { - return String.valueOf(getCount()); - } - } - ByteCounter outputCounter; - - void writeAllBandsTo(OutputStream out) throws IOException { - // Wrap a byte-counter around the output stream. - outputCounter = new ByteCounter(out); - out = outputCounter; - all_bands.writeTo(out); - if (verbose > 0) { - long nbytes = outputCounter.getCount(); - Utils.log.info("Wrote total of "+nbytes+" bytes."); - assert(nbytes == archiveSize0+archiveSize1); - } - outputCounter = null; - } - - // random AO_XXX bits, decoded from the archive header - protected int archiveOptions; - - // archiveSize1 sizes most of the archive [archive_options..file_bits). - protected long archiveSize0; // size through archive_size_lo - protected long archiveSize1; // size reported in archive_header - protected int archiveNextCount; // reported in archive_header - - static final int AH_LENGTH_0 = 3; // archive_header_0 = {minver, majver, options} - static final int AH_LENGTH_MIN = 15; // observed in spec {header_0[3], cp_counts[8], class_counts[4]} - // Length contributions from optional archive size fields: - static final int AH_LENGTH_S = 2; // archive_header_S = optional {size_hi, size_lo} - static final int AH_ARCHIVE_SIZE_HI = 0; // offset in archive_header_S - static final int AH_ARCHIVE_SIZE_LO = 1; // offset in archive_header_S - // Length contributions from optional header fields: - static final int AH_FILE_HEADER_LEN = 5; // file_counts = {{size_hi, size_lo}, next, modtime, files} - static final int AH_SPECIAL_FORMAT_LEN = 2; // special_counts = {layouts, band_headers} - static final int AH_CP_NUMBER_LEN = 4; // cp_number_counts = {int, float, long, double} - static final int AH_CP_EXTRA_LEN = 4; // cp_attr_counts = {MH, MT, InDy, BSM} - - // Common structure of attribute band groups: - static final int AB_FLAGS_HI = 0; - static final int AB_FLAGS_LO = 1; - static final int AB_ATTR_COUNT = 2; - static final int AB_ATTR_INDEXES = 3; - static final int AB_ATTR_CALLS = 4; - - static IntBand getAttrBand(MultiBand xxx_attr_bands, int which) { - IntBand b = (IntBand) xxx_attr_bands.get(which); - switch (which) { - case AB_FLAGS_HI: - assert(b.name().endsWith("_flags_hi")); break; - case AB_FLAGS_LO: - assert(b.name().endsWith("_flags_lo")); break; - case AB_ATTR_COUNT: - assert(b.name().endsWith("_attr_count")); break; - case AB_ATTR_INDEXES: - assert(b.name().endsWith("_attr_indexes")); break; - case AB_ATTR_CALLS: - assert(b.name().endsWith("_attr_calls")); break; - default: - assert(false); break; - } - return b; - } - - private static final boolean NULL_IS_OK = true; - - MultiBand all_bands = (MultiBand) new MultiBand("(package)", UNSIGNED5).init(); - - // file header (various random bytes) - ByteBand archive_magic = all_bands.newByteBand("archive_magic"); - IntBand archive_header_0 = all_bands.newIntBand("archive_header_0", UNSIGNED5); - IntBand archive_header_S = all_bands.newIntBand("archive_header_S", UNSIGNED5); - IntBand archive_header_1 = all_bands.newIntBand("archive_header_1", UNSIGNED5); - ByteBand band_headers = all_bands.newByteBand("band_headers"); - - // constant pool contents - MultiBand cp_bands = all_bands.newMultiBand("(constant_pool)", DELTA5); - IntBand cp_Utf8_prefix = cp_bands.newIntBand("cp_Utf8_prefix"); - IntBand cp_Utf8_suffix = cp_bands.newIntBand("cp_Utf8_suffix", UNSIGNED5); - IntBand cp_Utf8_chars = cp_bands.newIntBand("cp_Utf8_chars", CHAR3); - IntBand cp_Utf8_big_suffix = cp_bands.newIntBand("cp_Utf8_big_suffix"); - MultiBand cp_Utf8_big_chars = cp_bands.newMultiBand("(cp_Utf8_big_chars)", DELTA5); - IntBand cp_Int = cp_bands.newIntBand("cp_Int", UDELTA5); - IntBand cp_Float = cp_bands.newIntBand("cp_Float", UDELTA5); - IntBand cp_Long_hi = cp_bands.newIntBand("cp_Long_hi", UDELTA5); - IntBand cp_Long_lo = cp_bands.newIntBand("cp_Long_lo"); - IntBand cp_Double_hi = cp_bands.newIntBand("cp_Double_hi", UDELTA5); - IntBand cp_Double_lo = cp_bands.newIntBand("cp_Double_lo"); - CPRefBand cp_String = cp_bands.newCPRefBand("cp_String", UDELTA5, CONSTANT_Utf8); - CPRefBand cp_Class = cp_bands.newCPRefBand("cp_Class", UDELTA5, CONSTANT_Utf8); - CPRefBand cp_Signature_form = cp_bands.newCPRefBand("cp_Signature_form", CONSTANT_Utf8); - CPRefBand cp_Signature_classes = cp_bands.newCPRefBand("cp_Signature_classes", UDELTA5, CONSTANT_Class); - CPRefBand cp_Descr_name = cp_bands.newCPRefBand("cp_Descr_name", CONSTANT_Utf8); - CPRefBand cp_Descr_type = cp_bands.newCPRefBand("cp_Descr_type", UDELTA5, CONSTANT_Signature); - CPRefBand cp_Field_class = cp_bands.newCPRefBand("cp_Field_class", CONSTANT_Class); - CPRefBand cp_Field_desc = cp_bands.newCPRefBand("cp_Field_desc", UDELTA5, CONSTANT_NameandType); - CPRefBand cp_Method_class = cp_bands.newCPRefBand("cp_Method_class", CONSTANT_Class); - CPRefBand cp_Method_desc = cp_bands.newCPRefBand("cp_Method_desc", UDELTA5, CONSTANT_NameandType); - CPRefBand cp_Imethod_class = cp_bands.newCPRefBand("cp_Imethod_class", CONSTANT_Class); - CPRefBand cp_Imethod_desc = cp_bands.newCPRefBand("cp_Imethod_desc", UDELTA5, CONSTANT_NameandType); - IntBand cp_MethodHandle_refkind = cp_bands.newIntBand("cp_MethodHandle_refkind", DELTA5); - CPRefBand cp_MethodHandle_member = cp_bands.newCPRefBand("cp_MethodHandle_member", UDELTA5, CONSTANT_AnyMember); - CPRefBand cp_MethodType = cp_bands.newCPRefBand("cp_MethodType", UDELTA5, CONSTANT_Signature); - CPRefBand cp_BootstrapMethod_ref = cp_bands.newCPRefBand("cp_BootstrapMethod_ref", DELTA5, CONSTANT_MethodHandle); - IntBand cp_BootstrapMethod_arg_count = cp_bands.newIntBand("cp_BootstrapMethod_arg_count", UDELTA5); - CPRefBand cp_BootstrapMethod_arg = cp_bands.newCPRefBand("cp_BootstrapMethod_arg", DELTA5, CONSTANT_LoadableValue); - CPRefBand cp_InvokeDynamic_spec = cp_bands.newCPRefBand("cp_InvokeDynamic_spec", DELTA5, CONSTANT_BootstrapMethod); - CPRefBand cp_InvokeDynamic_desc = cp_bands.newCPRefBand("cp_InvokeDynamic_desc", UDELTA5, CONSTANT_NameandType); - - // bands for carrying attribute definitions: - MultiBand attr_definition_bands = all_bands.newMultiBand("(attr_definition_bands)", UNSIGNED5); - ByteBand attr_definition_headers = attr_definition_bands.newByteBand("attr_definition_headers"); - CPRefBand attr_definition_name = attr_definition_bands.newCPRefBand("attr_definition_name", CONSTANT_Utf8); - CPRefBand attr_definition_layout = attr_definition_bands.newCPRefBand("attr_definition_layout", CONSTANT_Utf8); - - // bands for hardwired InnerClasses attribute (shared across the package) - MultiBand ic_bands = all_bands.newMultiBand("(ic_bands)", DELTA5); - CPRefBand ic_this_class = ic_bands.newCPRefBand("ic_this_class", UDELTA5, CONSTANT_Class); - IntBand ic_flags = ic_bands.newIntBand("ic_flags", UNSIGNED5); - // These bands contain data only where flags sets ACC_IC_LONG_FORM: - CPRefBand ic_outer_class = ic_bands.newCPRefBand("ic_outer_class", DELTA5, CONSTANT_Class, NULL_IS_OK); - CPRefBand ic_name = ic_bands.newCPRefBand("ic_name", DELTA5, CONSTANT_Utf8, NULL_IS_OK); - - // bands for carrying class schema information: - MultiBand class_bands = all_bands.newMultiBand("(class_bands)", DELTA5); - CPRefBand class_this = class_bands.newCPRefBand("class_this", CONSTANT_Class); - CPRefBand class_super = class_bands.newCPRefBand("class_super", CONSTANT_Class); - IntBand class_interface_count = class_bands.newIntBand("class_interface_count"); - CPRefBand class_interface = class_bands.newCPRefBand("class_interface", CONSTANT_Class); - - // bands for class members - IntBand class_field_count = class_bands.newIntBand("class_field_count"); - IntBand class_method_count = class_bands.newIntBand("class_method_count"); - - CPRefBand field_descr = class_bands.newCPRefBand("field_descr", CONSTANT_NameandType); - MultiBand field_attr_bands = class_bands.newMultiBand("(field_attr_bands)", UNSIGNED5); - IntBand field_flags_hi = field_attr_bands.newIntBand("field_flags_hi"); - IntBand field_flags_lo = field_attr_bands.newIntBand("field_flags_lo"); - IntBand field_attr_count = field_attr_bands.newIntBand("field_attr_count"); - IntBand field_attr_indexes = field_attr_bands.newIntBand("field_attr_indexes"); - IntBand field_attr_calls = field_attr_bands.newIntBand("field_attr_calls"); - - // bands for predefined field attributes - CPRefBand field_ConstantValue_KQ = field_attr_bands.newCPRefBand("field_ConstantValue_KQ", CONSTANT_FieldSpecific); - CPRefBand field_Signature_RS = field_attr_bands.newCPRefBand("field_Signature_RS", CONSTANT_Signature); - MultiBand field_metadata_bands = field_attr_bands.newMultiBand("(field_metadata_bands)", UNSIGNED5); - MultiBand field_type_metadata_bands = field_attr_bands.newMultiBand("(field_type_metadata_bands)", UNSIGNED5); - - CPRefBand method_descr = class_bands.newCPRefBand("method_descr", MDELTA5, CONSTANT_NameandType); - MultiBand method_attr_bands = class_bands.newMultiBand("(method_attr_bands)", UNSIGNED5); - IntBand method_flags_hi = method_attr_bands.newIntBand("method_flags_hi"); - IntBand method_flags_lo = method_attr_bands.newIntBand("method_flags_lo"); - IntBand method_attr_count = method_attr_bands.newIntBand("method_attr_count"); - IntBand method_attr_indexes = method_attr_bands.newIntBand("method_attr_indexes"); - IntBand method_attr_calls = method_attr_bands.newIntBand("method_attr_calls"); - // band for predefined method attributes - IntBand method_Exceptions_N = method_attr_bands.newIntBand("method_Exceptions_N"); - CPRefBand method_Exceptions_RC = method_attr_bands.newCPRefBand("method_Exceptions_RC", CONSTANT_Class); - CPRefBand method_Signature_RS = method_attr_bands.newCPRefBand("method_Signature_RS", CONSTANT_Signature); - MultiBand method_metadata_bands = method_attr_bands.newMultiBand("(method_metadata_bands)", UNSIGNED5); - // band for predefine method parameters - IntBand method_MethodParameters_NB = method_attr_bands.newIntBand("method_MethodParameters_NB", BYTE1); - CPRefBand method_MethodParameters_name_RUN = method_attr_bands.newCPRefBand("method_MethodParameters_name_RUN", UNSIGNED5, CONSTANT_Utf8, NULL_IS_OK); - IntBand method_MethodParameters_flag_FH = method_attr_bands.newIntBand("method_MethodParameters_flag_FH"); - MultiBand method_type_metadata_bands = method_attr_bands.newMultiBand("(method_type_metadata_bands)", UNSIGNED5); - - MultiBand class_attr_bands = class_bands.newMultiBand("(class_attr_bands)", UNSIGNED5); - IntBand class_flags_hi = class_attr_bands.newIntBand("class_flags_hi"); - IntBand class_flags_lo = class_attr_bands.newIntBand("class_flags_lo"); - IntBand class_attr_count = class_attr_bands.newIntBand("class_attr_count"); - IntBand class_attr_indexes = class_attr_bands.newIntBand("class_attr_indexes"); - IntBand class_attr_calls = class_attr_bands.newIntBand("class_attr_calls"); - // band for predefined SourceFile and other class attributes - CPRefBand class_SourceFile_RUN = class_attr_bands.newCPRefBand("class_SourceFile_RUN", UNSIGNED5, CONSTANT_Utf8, NULL_IS_OK); - CPRefBand class_EnclosingMethod_RC = class_attr_bands.newCPRefBand("class_EnclosingMethod_RC", CONSTANT_Class); - CPRefBand class_EnclosingMethod_RDN = class_attr_bands.newCPRefBand("class_EnclosingMethod_RDN", UNSIGNED5, CONSTANT_NameandType, NULL_IS_OK); - CPRefBand class_Signature_RS = class_attr_bands.newCPRefBand("class_Signature_RS", CONSTANT_Signature); - MultiBand class_metadata_bands = class_attr_bands.newMultiBand("(class_metadata_bands)", UNSIGNED5); - IntBand class_InnerClasses_N = class_attr_bands.newIntBand("class_InnerClasses_N"); - CPRefBand class_InnerClasses_RC = class_attr_bands.newCPRefBand("class_InnerClasses_RC", CONSTANT_Class); - IntBand class_InnerClasses_F = class_attr_bands.newIntBand("class_InnerClasses_F"); - CPRefBand class_InnerClasses_outer_RCN = class_attr_bands.newCPRefBand("class_InnerClasses_outer_RCN", UNSIGNED5, CONSTANT_Class, NULL_IS_OK); - CPRefBand class_InnerClasses_name_RUN = class_attr_bands.newCPRefBand("class_InnerClasses_name_RUN", UNSIGNED5, CONSTANT_Utf8, NULL_IS_OK); - IntBand class_ClassFile_version_minor_H = class_attr_bands.newIntBand("class_ClassFile_version_minor_H"); - IntBand class_ClassFile_version_major_H = class_attr_bands.newIntBand("class_ClassFile_version_major_H"); - MultiBand class_type_metadata_bands = class_attr_bands.newMultiBand("(class_type_metadata_bands)", UNSIGNED5); - - MultiBand code_bands = class_bands.newMultiBand("(code_bands)", UNSIGNED5); - ByteBand code_headers = code_bands.newByteBand("code_headers"); //BYTE1 - IntBand code_max_stack = code_bands.newIntBand("code_max_stack", UNSIGNED5); - IntBand code_max_na_locals = code_bands.newIntBand("code_max_na_locals", UNSIGNED5); - IntBand code_handler_count = code_bands.newIntBand("code_handler_count", UNSIGNED5); - IntBand code_handler_start_P = code_bands.newIntBand("code_handler_start_P", BCI5); - IntBand code_handler_end_PO = code_bands.newIntBand("code_handler_end_PO", BRANCH5); - IntBand code_handler_catch_PO = code_bands.newIntBand("code_handler_catch_PO", BRANCH5); - CPRefBand code_handler_class_RCN = code_bands.newCPRefBand("code_handler_class_RCN", UNSIGNED5, CONSTANT_Class, NULL_IS_OK); - - MultiBand code_attr_bands = class_bands.newMultiBand("(code_attr_bands)", UNSIGNED5); - IntBand code_flags_hi = code_attr_bands.newIntBand("code_flags_hi"); - IntBand code_flags_lo = code_attr_bands.newIntBand("code_flags_lo"); - IntBand code_attr_count = code_attr_bands.newIntBand("code_attr_count"); - IntBand code_attr_indexes = code_attr_bands.newIntBand("code_attr_indexes"); - IntBand code_attr_calls = code_attr_bands.newIntBand("code_attr_calls"); - - MultiBand stackmap_bands = code_attr_bands.newMultiBand("(StackMapTable_bands)", UNSIGNED5); - IntBand code_StackMapTable_N = stackmap_bands.newIntBand("code_StackMapTable_N"); - IntBand code_StackMapTable_frame_T = stackmap_bands.newIntBand("code_StackMapTable_frame_T",BYTE1); - IntBand code_StackMapTable_local_N = stackmap_bands.newIntBand("code_StackMapTable_local_N"); - IntBand code_StackMapTable_stack_N = stackmap_bands.newIntBand("code_StackMapTable_stack_N"); - IntBand code_StackMapTable_offset = stackmap_bands.newIntBand("code_StackMapTable_offset", UNSIGNED5); - IntBand code_StackMapTable_T = stackmap_bands.newIntBand("code_StackMapTable_T", BYTE1); - CPRefBand code_StackMapTable_RC = stackmap_bands.newCPRefBand("code_StackMapTable_RC", CONSTANT_Class); - IntBand code_StackMapTable_P = stackmap_bands.newIntBand("code_StackMapTable_P", BCI5); - - // bands for predefined LineNumberTable attribute - IntBand code_LineNumberTable_N = code_attr_bands.newIntBand("code_LineNumberTable_N"); - IntBand code_LineNumberTable_bci_P = code_attr_bands.newIntBand("code_LineNumberTable_bci_P", BCI5); - IntBand code_LineNumberTable_line = code_attr_bands.newIntBand("code_LineNumberTable_line"); - - // bands for predefined LocalVariable{Type}Table attributes - IntBand code_LocalVariableTable_N = code_attr_bands.newIntBand("code_LocalVariableTable_N"); - IntBand code_LocalVariableTable_bci_P = code_attr_bands.newIntBand("code_LocalVariableTable_bci_P", BCI5); - IntBand code_LocalVariableTable_span_O = code_attr_bands.newIntBand("code_LocalVariableTable_span_O", BRANCH5); - CPRefBand code_LocalVariableTable_name_RU = code_attr_bands.newCPRefBand("code_LocalVariableTable_name_RU", CONSTANT_Utf8); - CPRefBand code_LocalVariableTable_type_RS = code_attr_bands.newCPRefBand("code_LocalVariableTable_type_RS", CONSTANT_Signature); - IntBand code_LocalVariableTable_slot = code_attr_bands.newIntBand("code_LocalVariableTable_slot"); - IntBand code_LocalVariableTypeTable_N = code_attr_bands.newIntBand("code_LocalVariableTypeTable_N"); - IntBand code_LocalVariableTypeTable_bci_P = code_attr_bands.newIntBand("code_LocalVariableTypeTable_bci_P", BCI5); - IntBand code_LocalVariableTypeTable_span_O = code_attr_bands.newIntBand("code_LocalVariableTypeTable_span_O", BRANCH5); - CPRefBand code_LocalVariableTypeTable_name_RU = code_attr_bands.newCPRefBand("code_LocalVariableTypeTable_name_RU", CONSTANT_Utf8); - CPRefBand code_LocalVariableTypeTable_type_RS = code_attr_bands.newCPRefBand("code_LocalVariableTypeTable_type_RS", CONSTANT_Signature); - IntBand code_LocalVariableTypeTable_slot = code_attr_bands.newIntBand("code_LocalVariableTypeTable_slot"); - MultiBand code_type_metadata_bands = code_attr_bands.newMultiBand("(code_type_metadata_bands)", UNSIGNED5); - - // bands for bytecodes - MultiBand bc_bands = all_bands.newMultiBand("(byte_codes)", UNSIGNED5); - ByteBand bc_codes = bc_bands.newByteBand("bc_codes"); //BYTE1 - // remaining bands provide typed opcode fields required by the bc_codes - - IntBand bc_case_count = bc_bands.newIntBand("bc_case_count"); // *switch - IntBand bc_case_value = bc_bands.newIntBand("bc_case_value", DELTA5); // *switch - ByteBand bc_byte = bc_bands.newByteBand("bc_byte"); //BYTE1 // bipush, iinc, *newarray - IntBand bc_short = bc_bands.newIntBand("bc_short", DELTA5); // sipush, wide iinc - IntBand bc_local = bc_bands.newIntBand("bc_local"); // *load, *store, iinc, ret - IntBand bc_label = bc_bands.newIntBand("bc_label", BRANCH5); // if*, goto*, jsr*, *switch - - // Most CP refs exhibit some correlation, and benefit from delta coding. - // The notable exceptions are class and method references. - - // ldc* operands: - CPRefBand bc_intref = bc_bands.newCPRefBand("bc_intref", DELTA5, CONSTANT_Integer); - CPRefBand bc_floatref = bc_bands.newCPRefBand("bc_floatref", DELTA5, CONSTANT_Float); - CPRefBand bc_longref = bc_bands.newCPRefBand("bc_longref", DELTA5, CONSTANT_Long); - CPRefBand bc_doubleref = bc_bands.newCPRefBand("bc_doubleref", DELTA5, CONSTANT_Double); - CPRefBand bc_stringref = bc_bands.newCPRefBand("bc_stringref", DELTA5, CONSTANT_String); - CPRefBand bc_loadablevalueref = bc_bands.newCPRefBand("bc_loadablevalueref", DELTA5, CONSTANT_LoadableValue); - - // nulls produced by bc_classref are taken to mean the current class - CPRefBand bc_classref = bc_bands.newCPRefBand("bc_classref", UNSIGNED5, CONSTANT_Class, NULL_IS_OK); // new, *anew*, c*cast, i*of, ldc - CPRefBand bc_fieldref = bc_bands.newCPRefBand("bc_fieldref", DELTA5, CONSTANT_Fieldref); // get*, put* - CPRefBand bc_methodref = bc_bands.newCPRefBand("bc_methodref", CONSTANT_Methodref); // invoke[vs]* - CPRefBand bc_imethodref = bc_bands.newCPRefBand("bc_imethodref", DELTA5, CONSTANT_InterfaceMethodref); // invokeinterface - CPRefBand bc_indyref = bc_bands.newCPRefBand("bc_indyref", DELTA5, CONSTANT_InvokeDynamic); // invokedynamic - - // _self_linker_op family - CPRefBand bc_thisfield = bc_bands.newCPRefBand("bc_thisfield", CONSTANT_None); // any field within cur. class - CPRefBand bc_superfield = bc_bands.newCPRefBand("bc_superfield", CONSTANT_None); // any field within superclass - CPRefBand bc_thismethod = bc_bands.newCPRefBand("bc_thismethod", CONSTANT_None); // any method within cur. class - CPRefBand bc_supermethod = bc_bands.newCPRefBand("bc_supermethod", CONSTANT_None); // any method within superclass - // bc_invokeinit family: - IntBand bc_initref = bc_bands.newIntBand("bc_initref"); - // escapes - CPRefBand bc_escref = bc_bands.newCPRefBand("bc_escref", CONSTANT_All); - IntBand bc_escrefsize = bc_bands.newIntBand("bc_escrefsize"); - IntBand bc_escsize = bc_bands.newIntBand("bc_escsize"); - ByteBand bc_escbyte = bc_bands.newByteBand("bc_escbyte"); - - // bands for carrying resource files and file attributes: - MultiBand file_bands = all_bands.newMultiBand("(file_bands)", UNSIGNED5); - CPRefBand file_name = file_bands.newCPRefBand("file_name", CONSTANT_Utf8); - IntBand file_size_hi = file_bands.newIntBand("file_size_hi"); - IntBand file_size_lo = file_bands.newIntBand("file_size_lo"); - IntBand file_modtime = file_bands.newIntBand("file_modtime", DELTA5); - IntBand file_options = file_bands.newIntBand("file_options"); - ByteBand file_bits = file_bands.newByteBand("file_bits"); - - // End of band definitions! - - /** Given CP indexes, distribute tag-specific indexes to bands. */ - protected void setBandIndexes() { - // Handle prior calls to setBandIndex: - for (Object[] need : needPredefIndex) { - CPRefBand b = (CPRefBand) need[0]; - Byte which = (Byte) need[1]; - b.setIndex(getCPIndex(which.byteValue())); - } - needPredefIndex = null; // no more predefs - - if (verbose > 3) { - printCDecl(all_bands); - } - } - - protected void setBandIndex(CPRefBand b, byte which) { - Object[] need = { b, Byte.valueOf(which) }; - if (which == CONSTANT_FieldSpecific) { - // I.e., attribute layouts KQ (no null) or KQN (null ok). - allKQBands.add(b); - } else if (needPredefIndex != null) { - needPredefIndex.add(need); - } else { - // Not in predefinition mode; getCPIndex now works. - b.setIndex(getCPIndex(which)); - } - } - - protected void setConstantValueIndex(Field f) { - Index ix = null; - if (f != null) { - byte tag = f.getLiteralTag(); - ix = getCPIndex(tag); - if (verbose > 2) - Utils.log.fine("setConstantValueIndex "+f+" "+ConstantPool.tagName(tag)+" => "+ix); - assert(ix != null); - } - // Typically, allKQBands is the singleton of field_ConstantValue_KQ. - for (CPRefBand xxx_KQ : allKQBands) { - xxx_KQ.setIndex(ix); - } - } - - // Table of bands which contain metadata. - protected MultiBand[] metadataBands = new MultiBand[ATTR_CONTEXT_LIMIT]; - { - metadataBands[ATTR_CONTEXT_CLASS] = class_metadata_bands; - metadataBands[ATTR_CONTEXT_FIELD] = field_metadata_bands; - metadataBands[ATTR_CONTEXT_METHOD] = method_metadata_bands; - } - // Table of bands which contains type_metadata (TypeAnnotations) - protected MultiBand[] typeMetadataBands = new MultiBand[ATTR_CONTEXT_LIMIT]; - { - typeMetadataBands[ATTR_CONTEXT_CLASS] = class_type_metadata_bands; - typeMetadataBands[ATTR_CONTEXT_FIELD] = field_type_metadata_bands; - typeMetadataBands[ATTR_CONTEXT_METHOD] = method_type_metadata_bands; - typeMetadataBands[ATTR_CONTEXT_CODE] = code_type_metadata_bands; - } - - // Attribute layouts. - public static final int ADH_CONTEXT_MASK = 0x3; // (ad_hdr & ADH_CONTEXT_MASK) - public static final int ADH_BIT_SHIFT = 0x2; // (ad_hdr >> ADH_BIT_SHIFT) - public static final int ADH_BIT_IS_LSB = 1; - public static final int ATTR_INDEX_OVERFLOW = -1; - - public int[] attrIndexLimit = new int[ATTR_CONTEXT_LIMIT]; - // Each index limit is either 32 or 63, depending on AO_HAVE_XXX_FLAGS_HI. - - // Which flag bits are taken over by attributes? - protected long[] attrFlagMask = new long[ATTR_CONTEXT_LIMIT]; - // Which flag bits have been taken over explicitly? - protected long[] attrDefSeen = new long[ATTR_CONTEXT_LIMIT]; - - // What pseudo-attribute bits are there to watch for? - protected int[] attrOverflowMask = new int[ATTR_CONTEXT_LIMIT]; - protected int attrClassFileVersionMask; - - // Mapping from Attribute.Layout to Band[] (layout element bands). - protected Map<Attribute.Layout, Band[]> attrBandTable = new HashMap<>(); - - // Well-known attributes: - protected final Attribute.Layout attrCodeEmpty; - protected final Attribute.Layout attrInnerClassesEmpty; - protected final Attribute.Layout attrClassFileVersion; - protected final Attribute.Layout attrConstantValue; - - // Mapping from Attribute.Layout to Integer (inverse of attrDefs) - Map<Attribute.Layout, Integer> attrIndexTable = new HashMap<>(); - - // Mapping from attribute index (<32 are flag bits) to attributes. - protected List<List<Attribute.Layout>> attrDefs = - new FixedList<>(ATTR_CONTEXT_LIMIT); - { - for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { - assert(attrIndexLimit[i] == 0); - attrIndexLimit[i] = 32; // just for the sake of predefs. - attrDefs.set(i, new ArrayList<>(Collections.nCopies( - attrIndexLimit[i], (Attribute.Layout)null))); - - } - - // Add predefined attribute definitions: - attrInnerClassesEmpty = - predefineAttribute(CLASS_ATTR_InnerClasses, ATTR_CONTEXT_CLASS, null, - "InnerClasses", ""); - assert(attrInnerClassesEmpty == Package.attrInnerClassesEmpty); - predefineAttribute(CLASS_ATTR_SourceFile, ATTR_CONTEXT_CLASS, - new Band[] { class_SourceFile_RUN }, - "SourceFile", "RUNH"); - predefineAttribute(CLASS_ATTR_EnclosingMethod, ATTR_CONTEXT_CLASS, - new Band[] { - class_EnclosingMethod_RC, - class_EnclosingMethod_RDN - }, - "EnclosingMethod", "RCHRDNH"); - attrClassFileVersion = - predefineAttribute(CLASS_ATTR_ClassFile_version, ATTR_CONTEXT_CLASS, - new Band[] { - class_ClassFile_version_minor_H, - class_ClassFile_version_major_H - }, - ".ClassFile.version", "HH"); - predefineAttribute(X_ATTR_Signature, ATTR_CONTEXT_CLASS, - new Band[] { class_Signature_RS }, - "Signature", "RSH"); - predefineAttribute(X_ATTR_Deprecated, ATTR_CONTEXT_CLASS, null, - "Deprecated", ""); - //predefineAttribute(X_ATTR_Synthetic, ATTR_CONTEXT_CLASS, null, - // "Synthetic", ""); - predefineAttribute(X_ATTR_OVERFLOW, ATTR_CONTEXT_CLASS, null, - ".Overflow", ""); - attrConstantValue = - predefineAttribute(FIELD_ATTR_ConstantValue, ATTR_CONTEXT_FIELD, - new Band[] { field_ConstantValue_KQ }, - "ConstantValue", "KQH"); - predefineAttribute(X_ATTR_Signature, ATTR_CONTEXT_FIELD, - new Band[] { field_Signature_RS }, - "Signature", "RSH"); - predefineAttribute(X_ATTR_Deprecated, ATTR_CONTEXT_FIELD, null, - "Deprecated", ""); - //predefineAttribute(X_ATTR_Synthetic, ATTR_CONTEXT_FIELD, null, - // "Synthetic", ""); - predefineAttribute(X_ATTR_OVERFLOW, ATTR_CONTEXT_FIELD, null, - ".Overflow", ""); - attrCodeEmpty = - predefineAttribute(METHOD_ATTR_Code, ATTR_CONTEXT_METHOD, null, - "Code", ""); - predefineAttribute(METHOD_ATTR_Exceptions, ATTR_CONTEXT_METHOD, - new Band[] { - method_Exceptions_N, - method_Exceptions_RC - }, - "Exceptions", "NH[RCH]"); - predefineAttribute(METHOD_ATTR_MethodParameters, ATTR_CONTEXT_METHOD, - new Band[]{ - method_MethodParameters_NB, - method_MethodParameters_name_RUN, - method_MethodParameters_flag_FH - }, - "MethodParameters", "NB[RUNHFH]"); - assert(attrCodeEmpty == Package.attrCodeEmpty); - predefineAttribute(X_ATTR_Signature, ATTR_CONTEXT_METHOD, - new Band[] { method_Signature_RS }, - "Signature", "RSH"); - predefineAttribute(X_ATTR_Deprecated, ATTR_CONTEXT_METHOD, null, - "Deprecated", ""); - //predefineAttribute(X_ATTR_Synthetic, ATTR_CONTEXT_METHOD, null, - // "Synthetic", ""); - predefineAttribute(X_ATTR_OVERFLOW, ATTR_CONTEXT_METHOD, null, - ".Overflow", ""); - - for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { - MultiBand xxx_metadata_bands = metadataBands[ctype]; - if (ctype != ATTR_CONTEXT_CODE) { - // These arguments cause the bands to be built - // automatically for this complicated layout: - predefineAttribute(X_ATTR_RuntimeVisibleAnnotations, - ATTR_CONTEXT_NAME[ctype]+"_RVA_", - xxx_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeVisibleAnnotations")); - predefineAttribute(X_ATTR_RuntimeInvisibleAnnotations, - ATTR_CONTEXT_NAME[ctype]+"_RIA_", - xxx_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeInvisibleAnnotations")); - - if (ctype == ATTR_CONTEXT_METHOD) { - predefineAttribute(METHOD_ATTR_RuntimeVisibleParameterAnnotations, - "method_RVPA_", xxx_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeVisibleParameterAnnotations")); - predefineAttribute(METHOD_ATTR_RuntimeInvisibleParameterAnnotations, - "method_RIPA_", xxx_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeInvisibleParameterAnnotations")); - predefineAttribute(METHOD_ATTR_AnnotationDefault, - "method_AD_", xxx_metadata_bands, - Attribute.lookup(null, ctype, - "AnnotationDefault")); - } - } - // All contexts have these - MultiBand xxx_type_metadata_bands = typeMetadataBands[ctype]; - predefineAttribute(X_ATTR_RuntimeVisibleTypeAnnotations, - ATTR_CONTEXT_NAME[ctype] + "_RVTA_", - xxx_type_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeVisibleTypeAnnotations")); - predefineAttribute(X_ATTR_RuntimeInvisibleTypeAnnotations, - ATTR_CONTEXT_NAME[ctype] + "_RITA_", - xxx_type_metadata_bands, - Attribute.lookup(null, ctype, - "RuntimeInvisibleTypeAnnotations")); - } - - - Attribute.Layout stackMapDef = Attribute.lookup(null, ATTR_CONTEXT_CODE, "StackMapTable").layout(); - predefineAttribute(CODE_ATTR_StackMapTable, ATTR_CONTEXT_CODE, - stackmap_bands.toArray(), - stackMapDef.name(), stackMapDef.layout()); - - predefineAttribute(CODE_ATTR_LineNumberTable, ATTR_CONTEXT_CODE, - new Band[] { - code_LineNumberTable_N, - code_LineNumberTable_bci_P, - code_LineNumberTable_line - }, - "LineNumberTable", "NH[PHH]"); - predefineAttribute(CODE_ATTR_LocalVariableTable, ATTR_CONTEXT_CODE, - new Band[] { - code_LocalVariableTable_N, - code_LocalVariableTable_bci_P, - code_LocalVariableTable_span_O, - code_LocalVariableTable_name_RU, - code_LocalVariableTable_type_RS, - code_LocalVariableTable_slot - }, - "LocalVariableTable", "NH[PHOHRUHRSHH]"); - predefineAttribute(CODE_ATTR_LocalVariableTypeTable, ATTR_CONTEXT_CODE, - new Band[] { - code_LocalVariableTypeTable_N, - code_LocalVariableTypeTable_bci_P, - code_LocalVariableTypeTable_span_O, - code_LocalVariableTypeTable_name_RU, - code_LocalVariableTypeTable_type_RS, - code_LocalVariableTypeTable_slot - }, - "LocalVariableTypeTable", "NH[PHOHRUHRSHH]"); - predefineAttribute(X_ATTR_OVERFLOW, ATTR_CONTEXT_CODE, null, - ".Overflow", ""); - - // Clear the record of having seen these definitions, - // so they may be redefined without error. - for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { - attrDefSeen[i] = 0; - } - - // Set up the special masks: - for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { - attrOverflowMask[i] = (1<<X_ATTR_OVERFLOW); - attrIndexLimit[i] = 0; // will make a final decision later - } - attrClassFileVersionMask = (1<<CLASS_ATTR_ClassFile_version); - } - - private void adjustToClassVersion() throws IOException { - if (getHighestClassVersion().lessThan(JAVA6_MAX_CLASS_VERSION)) { - if (verbose > 0) Utils.log.fine("Legacy package version"); - // Revoke definition of pre-1.6 attribute type. - undefineAttribute(CODE_ATTR_StackMapTable, ATTR_CONTEXT_CODE); - } - } - - protected void initAttrIndexLimit() { - for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { - assert(attrIndexLimit[i] == 0); // decide on it now! - attrIndexLimit[i] = (haveFlagsHi(i)? 63: 32); - List<Attribute.Layout> defList = attrDefs.get(i); - assert(defList.size() == 32); // all predef indexes are <32 - int addMore = attrIndexLimit[i] - defList.size(); - defList.addAll(Collections.nCopies(addMore, (Attribute.Layout) null)); - } - } - - protected boolean haveFlagsHi(int ctype) { - int mask = 1<<(LG_AO_HAVE_XXX_FLAGS_HI+ctype); - switch (ctype) { - case ATTR_CONTEXT_CLASS: - assert(mask == AO_HAVE_CLASS_FLAGS_HI); break; - case ATTR_CONTEXT_FIELD: - assert(mask == AO_HAVE_FIELD_FLAGS_HI); break; - case ATTR_CONTEXT_METHOD: - assert(mask == AO_HAVE_METHOD_FLAGS_HI); break; - case ATTR_CONTEXT_CODE: - assert(mask == AO_HAVE_CODE_FLAGS_HI); break; - default: - assert(false); - } - return testBit(archiveOptions, mask); - } - - protected List<Attribute.Layout> getPredefinedAttrs(int ctype) { - assert(attrIndexLimit[ctype] != 0); - List<Attribute.Layout> res = new ArrayList<>(attrIndexLimit[ctype]); - // Remove nulls and non-predefs. - for (int ai = 0; ai < attrIndexLimit[ctype]; ai++) { - if (testBit(attrDefSeen[ctype], 1L<<ai)) continue; - Attribute.Layout def = attrDefs.get(ctype).get(ai); - if (def == null) continue; // unused flag bit - assert(isPredefinedAttr(ctype, ai)); - res.add(def); - } - return res; - } - - protected boolean isPredefinedAttr(int ctype, int ai) { - assert(attrIndexLimit[ctype] != 0); - // Overflow attrs are never predefined. - if (ai >= attrIndexLimit[ctype]) return false; - // If the bit is set, it was explicitly def'd. - if (testBit(attrDefSeen[ctype], 1L<<ai)) return false; - return (attrDefs.get(ctype).get(ai) != null); - } - - protected void adjustSpecialAttrMasks() { - // Clear special masks if new definitions have been seen for them. - attrClassFileVersionMask &= ~ attrDefSeen[ATTR_CONTEXT_CLASS]; - // It is possible to clear the overflow mask (bit 16). - for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { - attrOverflowMask[i] &= ~ attrDefSeen[i]; - } - } - - protected Attribute makeClassFileVersionAttr(Package.Version ver) { - return attrClassFileVersion.addContent(ver.asBytes()); - } - - protected Package.Version parseClassFileVersionAttr(Attribute attr) { - assert(attr.layout() == attrClassFileVersion); - assert(attr.size() == 4); - return Package.Version.of(attr.bytes()); - } - - private boolean assertBandOKForElems(Band[] ab, Attribute.Layout.Element[] elems) { - for (int i = 0; i < elems.length; i++) { - assert(assertBandOKForElem(ab, elems[i])); - } - return true; - } - private boolean assertBandOKForElem(Band[] ab, Attribute.Layout.Element e) { - Band b = null; - if (e.bandIndex != Attribute.NO_BAND_INDEX) - b = ab[e.bandIndex]; - Coding rc = UNSIGNED5; - boolean wantIntBand = true; - switch (e.kind) { - case Attribute.EK_INT: - if (e.flagTest(Attribute.EF_SIGN)) { - rc = SIGNED5; - } else if (e.len == 1) { - rc = BYTE1; - } - break; - case Attribute.EK_BCI: - if (!e.flagTest(Attribute.EF_DELTA)) { - rc = BCI5; - } else { - rc = BRANCH5; - } - break; - case Attribute.EK_BCO: - rc = BRANCH5; - break; - case Attribute.EK_FLAG: - if (e.len == 1) rc = BYTE1; - break; - case Attribute.EK_REPL: - if (e.len == 1) rc = BYTE1; - assertBandOKForElems(ab, e.body); - break; - case Attribute.EK_UN: - if (e.flagTest(Attribute.EF_SIGN)) { - rc = SIGNED5; - } else if (e.len == 1) { - rc = BYTE1; - } - assertBandOKForElems(ab, e.body); - break; - case Attribute.EK_CASE: - assert(b == null); - assertBandOKForElems(ab, e.body); - return true; // no direct band - case Attribute.EK_CALL: - assert(b == null); - return true; // no direct band - case Attribute.EK_CBLE: - assert(b == null); - assertBandOKForElems(ab, e.body); - return true; // no direct band - case Attribute.EK_REF: - wantIntBand = false; - assert(b instanceof CPRefBand); - assert(((CPRefBand)b).nullOK == e.flagTest(Attribute.EF_NULL)); - break; - default: assert(false); - } - assert(b.regularCoding == rc) - : (e+" // "+b); - if (wantIntBand) - assert(b instanceof IntBand); - return true; - } - - private - Attribute.Layout predefineAttribute(int index, int ctype, Band[] ab, - String name, String layout) { - // Use Attribute.find to get uniquification of layouts. - Attribute.Layout def = Attribute.find(ctype, name, layout).layout(); - //def.predef = true; - if (index >= 0) { - setAttributeLayoutIndex(def, index); - } - if (ab == null) { - ab = new Band[0]; - } - assert(attrBandTable.get(def) == null); // no redef - attrBandTable.put(def, ab); - assert(def.bandCount == ab.length) - : (def+" // "+Arrays.asList(ab)); - // Let's make sure the band types match: - assert(assertBandOKForElems(ab, def.elems)); - return def; - } - - // This version takes bandPrefix/addHere instead of prebuilt Band[] ab. - private - Attribute.Layout predefineAttribute(int index, - String bandPrefix, MultiBand addHere, - Attribute attr) { - //Attribute.Layout def = Attribute.find(ctype, name, layout).layout(); - Attribute.Layout def = attr.layout(); - int ctype = def.ctype(); - return predefineAttribute(index, ctype, - makeNewAttributeBands(bandPrefix, def, addHere), - def.name(), def.layout()); - } - - private - void undefineAttribute(int index, int ctype) { - if (verbose > 1) { - System.out.println("Removing predefined "+ATTR_CONTEXT_NAME[ctype]+ - " attribute on bit "+index); - } - List<Attribute.Layout> defList = attrDefs.get(ctype); - Attribute.Layout def = defList.get(index); - assert(def != null); - defList.set(index, null); - attrIndexTable.put(def, null); - // Clear the def bit. (For predefs, it's already clear.) - assert(index < 64); - attrDefSeen[ctype] &= ~(1L<<index); - attrFlagMask[ctype] &= ~(1L<<index); - Band[] ab = attrBandTable.get(def); - for (int j = 0; j < ab.length; j++) { - ab[j].doneWithUnusedBand(); - } - } - - // Bands which contain non-predefined attrs. - protected MultiBand[] attrBands = new MultiBand[ATTR_CONTEXT_LIMIT]; - { - attrBands[ATTR_CONTEXT_CLASS] = class_attr_bands; - attrBands[ATTR_CONTEXT_FIELD] = field_attr_bands; - attrBands[ATTR_CONTEXT_METHOD] = method_attr_bands; - attrBands[ATTR_CONTEXT_CODE] = code_attr_bands; - } - - // Create bands for all non-predefined attrs. - void makeNewAttributeBands() { - // Retract special flag bit bindings, if they were taken over. - adjustSpecialAttrMasks(); - - for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { - String cname = ATTR_CONTEXT_NAME[ctype]; - MultiBand xxx_attr_bands = attrBands[ctype]; - long defSeen = attrDefSeen[ctype]; - // Note: attrDefSeen is always a subset of attrFlagMask. - assert((defSeen & ~attrFlagMask[ctype]) == 0); - for (int i = 0; i < attrDefs.get(ctype).size(); i++) { - Attribute.Layout def = attrDefs.get(ctype).get(i); - if (def == null) continue; // unused flag bit - if (def.bandCount == 0) continue; // empty attr - if (i < attrIndexLimit[ctype] && !testBit(defSeen, 1L<<i)) { - // There are already predefined bands here. - assert(attrBandTable.get(def) != null); - continue; - } - int base = xxx_attr_bands.size(); - String pfx = cname+"_"+def.name()+"_"; // debug only - if (verbose > 1) - Utils.log.fine("Making new bands for "+def); - Band[] newAB = makeNewAttributeBands(pfx, def, - xxx_attr_bands); - assert(newAB.length == def.bandCount); - Band[] prevAB = attrBandTable.put(def, newAB); - if (prevAB != null) { - // We won't be using these predefined bands. - for (int j = 0; j < prevAB.length; j++) { - prevAB[j].doneWithUnusedBand(); - } - } - } - } - //System.out.println(prevForAssertMap); - } - private - Band[] makeNewAttributeBands(String pfx, Attribute.Layout def, - MultiBand addHere) { - int base = addHere.size(); - makeNewAttributeBands(pfx, def.elems, addHere); - int nb = addHere.size() - base; - Band[] newAB = new Band[nb]; - for (int i = 0; i < nb; i++) { - newAB[i] = addHere.get(base+i); - } - return newAB; - } - // Recursive helper, operates on a "body" or other sequence of elems: - private - void makeNewAttributeBands(String pfx, Attribute.Layout.Element[] elems, - MultiBand ab) { - for (int i = 0; i < elems.length; i++) { - Attribute.Layout.Element e = elems[i]; - String name = pfx+ab.size()+"_"+e.layout; - { - int tem; - if ((tem = name.indexOf('[')) > 0) - name = name.substring(0, tem); - if ((tem = name.indexOf('(')) > 0) - name = name.substring(0, tem); - if (name.endsWith("H")) - name = name.substring(0, name.length()-1); - } - Band nb; - switch (e.kind) { - case Attribute.EK_INT: - nb = newElemBand(e, name, ab); - break; - case Attribute.EK_BCI: - if (!e.flagTest(Attribute.EF_DELTA)) { - // PH: transmit R(bci), store bci - nb = ab.newIntBand(name, BCI5); - } else { - // POH: transmit D(R(bci)), store bci - nb = ab.newIntBand(name, BRANCH5); - } - // Note: No case for BYTE1 here. - break; - case Attribute.EK_BCO: - // OH: transmit D(R(bci)), store D(bci) - nb = ab.newIntBand(name, BRANCH5); - // Note: No case for BYTE1 here. - break; - case Attribute.EK_FLAG: - assert(!e.flagTest(Attribute.EF_SIGN)); - nb = newElemBand(e, name, ab); - break; - case Attribute.EK_REPL: - assert(!e.flagTest(Attribute.EF_SIGN)); - nb = newElemBand(e, name, ab); - makeNewAttributeBands(pfx, e.body, ab); - break; - case Attribute.EK_UN: - nb = newElemBand(e, name, ab); - makeNewAttributeBands(pfx, e.body, ab); - break; - case Attribute.EK_CASE: - if (!e.flagTest(Attribute.EF_BACK)) { - // If it's not a duplicate body, make the bands. - makeNewAttributeBands(pfx, e.body, ab); - } - continue; // no new band to make - case Attribute.EK_REF: - byte refKind = e.refKind; - boolean nullOK = e.flagTest(Attribute.EF_NULL); - nb = ab.newCPRefBand(name, UNSIGNED5, refKind, nullOK); - // Note: No case for BYTE1 here. - break; - case Attribute.EK_CALL: - continue; // no new band to make - case Attribute.EK_CBLE: - makeNewAttributeBands(pfx, e.body, ab); - continue; // no new band to make - default: assert(false); continue; - } - if (verbose > 1) { - Utils.log.fine("New attribute band "+nb); - } - } - } - private - Band newElemBand(Attribute.Layout.Element e, String name, MultiBand ab) { - if (e.flagTest(Attribute.EF_SIGN)) { - return ab.newIntBand(name, SIGNED5); - } else if (e.len == 1) { - return ab.newIntBand(name, BYTE1); // Not ByteBand, please. - } else { - return ab.newIntBand(name, UNSIGNED5); - } - } - - protected int setAttributeLayoutIndex(Attribute.Layout def, int index) { - int ctype = def.ctype; - assert(ATTR_INDEX_OVERFLOW <= index && index < attrIndexLimit[ctype]); - List<Attribute.Layout> defList = attrDefs.get(ctype); - if (index == ATTR_INDEX_OVERFLOW) { - // Overflow attribute. - index = defList.size(); - defList.add(def); - if (verbose > 0) - Utils.log.info("Adding new attribute at "+def +": "+index); - attrIndexTable.put(def, index); - return index; - } - - // Detect redefinitions: - if (testBit(attrDefSeen[ctype], 1L<<index)) { - throw new RuntimeException("Multiple explicit definition at "+index+": "+def); - } - attrDefSeen[ctype] |= (1L<<index); - - // Adding a new fixed attribute. - assert(0 <= index && index < attrIndexLimit[ctype]); - if (verbose > (attrClassFileVersionMask == 0? 2:0)) - Utils.log.fine("Fixing new attribute at "+index - +": "+def - +(defList.get(index) == null? "": - "; replacing "+defList.get(index))); - attrFlagMask[ctype] |= (1L<<index); - // Remove index binding of any previous fixed attr. - attrIndexTable.put(defList.get(index), null); - defList.set(index, def); - attrIndexTable.put(def, index); - return index; - } - - // encodings found in the code_headers band - private static final int[][] shortCodeLimits = { - { 12, 12 }, // s<12, l<12, e=0 [1..144] - { 8, 8 }, // s<8, l<8, e=1 [145..208] - { 7, 7 }, // s<7, l<7, e=2 [209..256] - }; - public final int shortCodeHeader_h_limit = shortCodeLimits.length; - - // return 0 if it won't encode, else a number in [1..255] - static int shortCodeHeader(Code code) { - int s = code.max_stack; - int l0 = code.max_locals; - int h = code.handler_class.length; - if (h >= shortCodeLimits.length) return LONG_CODE_HEADER; - int siglen = code.getMethod().getArgumentSize(); - assert(l0 >= siglen); // enough locals for signature! - if (l0 < siglen) return LONG_CODE_HEADER; - int l1 = l0 - siglen; // do not count locals required by the signature - int lims = shortCodeLimits[h][0]; - int liml = shortCodeLimits[h][1]; - if (s >= lims || l1 >= liml) return LONG_CODE_HEADER; - int sc = shortCodeHeader_h_base(h); - sc += s + lims*l1; - if (sc > 255) return LONG_CODE_HEADER; - assert(shortCodeHeader_max_stack(sc) == s); - assert(shortCodeHeader_max_na_locals(sc) == l1); - assert(shortCodeHeader_handler_count(sc) == h); - return sc; - } - - static final int LONG_CODE_HEADER = 0; - static int shortCodeHeader_handler_count(int sc) { - assert(sc > 0 && sc <= 255); - for (int h = 0; ; h++) { - if (sc < shortCodeHeader_h_base(h+1)) - return h; - } - } - static int shortCodeHeader_max_stack(int sc) { - int h = shortCodeHeader_handler_count(sc); - int lims = shortCodeLimits[h][0]; - return (sc - shortCodeHeader_h_base(h)) % lims; - } - static int shortCodeHeader_max_na_locals(int sc) { - int h = shortCodeHeader_handler_count(sc); - int lims = shortCodeLimits[h][0]; - return (sc - shortCodeHeader_h_base(h)) / lims; - } - - private static int shortCodeHeader_h_base(int h) { - assert(h <= shortCodeLimits.length); - int sc = 1; - for (int h0 = 0; h0 < h; h0++) { - int lims = shortCodeLimits[h0][0]; - int liml = shortCodeLimits[h0][1]; - sc += lims * liml; - } - return sc; - } - - // utilities for accessing the bc_label band: - protected void putLabel(IntBand bc_label, Code c, int pc, int targetPC) { - bc_label.putInt(c.encodeBCI(targetPC) - c.encodeBCI(pc)); - } - protected int getLabel(IntBand bc_label, Code c, int pc) { - return c.decodeBCI(bc_label.getInt() + c.encodeBCI(pc)); - } - - protected CPRefBand getCPRefOpBand(int bc) { - switch (Instruction.getCPRefOpTag(bc)) { - case CONSTANT_Class: - return bc_classref; - case CONSTANT_Fieldref: - return bc_fieldref; - case CONSTANT_Methodref: - return bc_methodref; - case CONSTANT_InterfaceMethodref: - return bc_imethodref; - case CONSTANT_InvokeDynamic: - return bc_indyref; - case CONSTANT_LoadableValue: - switch (bc) { - case _ildc: case _ildc_w: - return bc_intref; - case _fldc: case _fldc_w: - return bc_floatref; - case _lldc2_w: - return bc_longref; - case _dldc2_w: - return bc_doubleref; - case _sldc: case _sldc_w: - return bc_stringref; - case _cldc: case _cldc_w: - return bc_classref; - case _qldc: case _qldc_w: - return bc_loadablevalueref; - } - break; - } - assert(false); - return null; - } - - protected CPRefBand selfOpRefBand(int self_bc) { - assert(Instruction.isSelfLinkerOp(self_bc)); - int idx = (self_bc - _self_linker_op); - boolean isSuper = (idx >= _self_linker_super_flag); - if (isSuper) idx -= _self_linker_super_flag; - boolean isAload = (idx >= _self_linker_aload_flag); - if (isAload) idx -= _self_linker_aload_flag; - int origBC = _first_linker_op + idx; - boolean isField = Instruction.isFieldOp(origBC); - if (!isSuper) - return isField? bc_thisfield: bc_thismethod; - else - return isField? bc_superfield: bc_supermethod; - } - - //////////////////////////////////////////////////////////////////// - - static int nextSeqForDebug; - static File dumpDir = null; - static OutputStream getDumpStream(Band b, String ext) throws IOException { - return getDumpStream(b.name, b.seqForDebug, ext, b); - } - static OutputStream getDumpStream(Index ix, String ext) throws IOException { - if (ix.size() == 0) return new ByteArrayOutputStream(); - int seq = ConstantPool.TAG_ORDER[ix.cpMap[0].tag]; - return getDumpStream(ix.debugName, seq, ext, ix); - } - static OutputStream getDumpStream(String name, int seq, String ext, Object b) throws IOException { - if (dumpDir == null) { - dumpDir = File.createTempFile("BD_", "", new File(".")); - dumpDir.delete(); - if (dumpDir.mkdir()) - Utils.log.info("Dumping bands to "+dumpDir); - } - name = name.replace('(', ' ').replace(')', ' '); - name = name.replace('/', ' '); - name = name.replace('*', ' '); - name = name.trim().replace(' ','_'); - name = ((10000+seq) + "_" + name).substring(1); - File dumpFile = new File(dumpDir, name+ext); - Utils.log.info("Dumping "+b+" to "+dumpFile); - return new BufferedOutputStream(new FileOutputStream(dumpFile)); - } - - // DEBUG ONLY: Validate me at each length change. - static boolean assertCanChangeLength(Band b) { - switch (b.phase) { - case COLLECT_PHASE: - case READ_PHASE: - return true; - } - return false; - } - - // DEBUG ONLY: Validate a phase. - static boolean assertPhase(Band b, int phaseExpected) { - if (b.phase() != phaseExpected) { - Utils.log.warning("phase expected "+phaseExpected+" was "+b.phase()+" in "+b); - return false; - } - return true; - } - - - // DEBUG ONLY: Tells whether verbosity is turned on. - static int verbose() { - return Utils.currentPropMap().getInteger(Utils.DEBUG_VERBOSE); - } - - - // DEBUG ONLY: Validate me at each phase change. - static boolean assertPhaseChangeOK(Band b, int p0, int p1) { - switch (p0*10+p1) { - /// Writing phases: - case NO_PHASE*10+COLLECT_PHASE: - // Ready to collect data from the input classes. - assert(!b.isReader()); - assert(b.capacity() >= 0); - assert(b.length() == 0); - return true; - case COLLECT_PHASE*10+FROZEN_PHASE: - case FROZEN_PHASE*10+FROZEN_PHASE: - assert(b.length() == 0); - return true; - case COLLECT_PHASE*10+WRITE_PHASE: - case FROZEN_PHASE*10+WRITE_PHASE: - // Data is all collected. Ready to write bytes to disk. - return true; - case WRITE_PHASE*10+DONE_PHASE: - // Done writing to disk. Ready to reset, in principle. - return true; - - /// Reading phases: - case NO_PHASE*10+EXPECT_PHASE: - assert(b.isReader()); - assert(b.capacity() < 0); - return true; - case EXPECT_PHASE*10+READ_PHASE: - // Ready to read values from disk. - assert(Math.max(0,b.capacity()) >= b.valuesExpected()); - assert(b.length() <= 0); - return true; - case READ_PHASE*10+DISBURSE_PHASE: - // Ready to disburse values. - assert(b.valuesRemainingForDebug() == b.length()); - return true; - case DISBURSE_PHASE*10+DONE_PHASE: - // Done disbursing values. Ready to reset, in principle. - assert(assertDoneDisbursing(b)); - return true; - } - if (p0 == p1) - Utils.log.warning("Already in phase "+p0); - else - Utils.log.warning("Unexpected phase "+p0+" -> "+p1); - return false; - } - - private static boolean assertDoneDisbursing(Band b) { - if (b.phase != DISBURSE_PHASE) { - Utils.log.warning("assertDoneDisbursing: still in phase "+b.phase+": "+b); - if (verbose() <= 1) return false; // fail now - } - int left = b.valuesRemainingForDebug(); - if (left > 0) { - Utils.log.warning("assertDoneDisbursing: "+left+" values left in "+b); - if (verbose() <= 1) return false; // fail now - } - if (b instanceof MultiBand) { - MultiBand mb = (MultiBand) b; - for (int i = 0; i < mb.bandCount; i++) { - Band sub = mb.bands[i]; - if (sub.phase != DONE_PHASE) { - Utils.log.warning("assertDoneDisbursing: sub-band still in phase "+sub.phase+": "+sub); - if (verbose() <= 1) return false; // fail now - } - } - } - return true; - } - - private static void printCDecl(Band b) { - if (b instanceof MultiBand) { - MultiBand mb = (MultiBand) b; - for (int i = 0; i < mb.bandCount; i++) { - printCDecl(mb.bands[i]); - } - return; - } - String ixS = "NULL"; - if (b instanceof CPRefBand) { - Index ix = ((CPRefBand)b).index; - if (ix != null) ixS = "INDEX("+ix.debugName+")"; - } - Coding[] knownc = { BYTE1, CHAR3, BCI5, BRANCH5, UNSIGNED5, - UDELTA5, SIGNED5, DELTA5, MDELTA5 }; - String[] knowns = { "BYTE1", "CHAR3", "BCI5", "BRANCH5", "UNSIGNED5", - "UDELTA5", "SIGNED5", "DELTA5", "MDELTA5" }; - Coding rc = b.regularCoding; - int rci = Arrays.asList(knownc).indexOf(rc); - String cstr; - if (rci >= 0) - cstr = knowns[rci]; - else - cstr = "CODING"+rc.keyString(); - System.out.println(" BAND_INIT(\""+b.name()+"\"" - +", "+cstr+", "+ixS+"),"); - } - - private Map<Band, Band> prevForAssertMap; - - // DEBUG ONLY: Record something about the band order. - boolean notePrevForAssert(Band b, Band p) { - if (prevForAssertMap == null) - prevForAssertMap = new HashMap<>(); - prevForAssertMap.put(b, p); - return true; - } - - // DEBUG ONLY: Validate next input band, ensure bands are read in sequence - private boolean assertReadyToReadFrom(Band b, InputStream in) throws IOException { - Band p = prevForAssertMap.get(b); - // Any previous band must be done reading before this one starts. - if (p != null && phaseCmp(p.phase(), DISBURSE_PHASE) < 0) { - Utils.log.warning("Previous band not done reading."); - Utils.log.info(" Previous band: "+p); - Utils.log.info(" Next band: "+b); - assert(verbose > 0); // die unless verbose is true - } - String name = b.name; - if (optDebugBands && !name.startsWith("(")) { - assert(bandSequenceList != null); - // Verify synchronization between reader & writer: - String inName = bandSequenceList.removeFirst(); - // System.out.println("Reading: " + name); - if (!inName.equals(name)) { - Utils.log.warning("Expected " + name + " but read: " + inName); - return false; - } - Utils.log.info("Read band in sequence: " + name); - } - return true; - } - - // DEBUG ONLY: Make sure a bunch of cprefs are correct. - private boolean assertValidCPRefs(CPRefBand b) { - if (b.index == null) return true; - int limit = b.index.size()+1; - for (int i = 0; i < b.length(); i++) { - int v = b.valueAtForDebug(i); - if (v < 0 || v >= limit) { - Utils.log.warning("CP ref out of range "+ - "["+i+"] = "+v+" in "+b); - return false; - } - } - return true; - } - - /* - * DEBUG ONLY: write the bands to a list and read back the list in order, - * this works perfectly if we use the java packer and unpacker, typically - * this will work with --repack or if they are in the same jvm instance. - */ - static LinkedList<String> bandSequenceList = null; - private boolean assertReadyToWriteTo(Band b, OutputStream out) throws IOException { - Band p = prevForAssertMap.get(b); - // Any previous band must be done writing before this one starts. - if (p != null && phaseCmp(p.phase(), DONE_PHASE) < 0) { - Utils.log.warning("Previous band not done writing."); - Utils.log.info(" Previous band: "+p); - Utils.log.info(" Next band: "+b); - assert(verbose > 0); // die unless verbose is true - } - String name = b.name; - if (optDebugBands && !name.startsWith("(")) { - if (bandSequenceList == null) - bandSequenceList = new LinkedList<>(); - // Verify synchronization between reader & writer: - bandSequenceList.add(name); - // System.out.println("Writing: " + b); - } - return true; - } - - protected static boolean testBit(int flags, int bitMask) { - return (flags & bitMask) != 0; - } - protected static int setBit(int flags, int bitMask, boolean z) { - return z ? (flags | bitMask) : (flags &~ bitMask); - } - protected static boolean testBit(long flags, long bitMask) { - return (flags & bitMask) != 0; - } - protected static long setBit(long flags, long bitMask, boolean z) { - return z ? (flags | bitMask) : (flags &~ bitMask); - } - - - static void printArrayTo(PrintStream ps, int[] values, int start, int end) { - int len = end-start; - for (int i = 0; i < len; i++) { - if (i % 10 == 0) - ps.println(); - else - ps.print(" "); - ps.print(values[start+i]); - } - ps.println(); - } - - static void printArrayTo(PrintStream ps, Entry[] cpMap, int start, int end) { - printArrayTo(ps, cpMap, start, end, false); - } - static void printArrayTo(PrintStream ps, Entry[] cpMap, int start, int end, boolean showTags) { - StringBuffer buf = new StringBuffer(); - int len = end-start; - for (int i = 0; i < len; i++) { - Entry e = cpMap[start+i]; - ps.print(start+i); ps.print("="); - if (showTags) { ps.print(e.tag); ps.print(":"); } - String s = e.stringValue(); - buf.setLength(0); - for (int j = 0; j < s.length(); j++) { - char ch = s.charAt(j); - if (!(ch < ' ' || ch > '~' || ch == '\\')) { - buf.append(ch); - } else if (ch == '\\') { - buf.append("\\\\"); - } else if (ch == '\n') { - buf.append("\\n"); - } else if (ch == '\t') { - buf.append("\\t"); - } else if (ch == '\r') { - buf.append("\\r"); - } else { - String str = "000"+Integer.toHexString(ch); - buf.append("\\u").append(str.substring(str.length()-4)); - } - } - ps.println(buf); - } - } - - - // Utilities for reallocating: - protected static Object[] realloc(Object[] a, int len) { - java.lang.Class<?> elt = a.getClass().getComponentType(); - Object[] na = (Object[]) java.lang.reflect.Array.newInstance(elt, len); - System.arraycopy(a, 0, na, 0, Math.min(a.length, len)); - return na; - } - protected static Object[] realloc(Object[] a) { - return realloc(a, Math.max(10, a.length*2)); - } - - protected static int[] realloc(int[] a, int len) { - if (len == 0) return noInts; - if (a == null) return new int[len]; - int[] na = new int[len]; - System.arraycopy(a, 0, na, 0, Math.min(a.length, len)); - return na; - } - protected static int[] realloc(int[] a) { - return realloc(a, Math.max(10, a.length*2)); - } - - protected static byte[] realloc(byte[] a, int len) { - if (len == 0) return noBytes; - if (a == null) return new byte[len]; - byte[] na = new byte[len]; - System.arraycopy(a, 0, na, 0, Math.min(a.length, len)); - return na; - } - protected static byte[] realloc(byte[] a) { - return realloc(a, Math.max(10, a.length*2)); - } -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/ClassReader.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,619 +0,0 @@ -/* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; -import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; -import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; -import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; -import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; -import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; -import com.sun.java.util.jar.pack.Package.Class; -import com.sun.java.util.jar.pack.Package.InnerClass; -import java.io.DataInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Map; -import static com.sun.java.util.jar.pack.Constants.*; - -/** - * Reader for a class file that is being incorporated into a package. - * @author John Rose - */ -class ClassReader { - int verbose; - - Package pkg; - Class cls; - long inPos; - long constantPoolLimit = -1; - DataInputStream in; - Map<Attribute.Layout, Attribute> attrDefs; - Map<Attribute.Layout, String> attrCommands; - String unknownAttrCommand = "error";; - - ClassReader(Class cls, InputStream in) throws IOException { - this.pkg = cls.getPackage(); - this.cls = cls; - this.verbose = pkg.verbose; - this.in = new DataInputStream(new FilterInputStream(in) { - public int read(byte b[], int off, int len) throws IOException { - int nr = super.read(b, off, len); - if (nr >= 0) inPos += nr; - return nr; - } - public int read() throws IOException { - int ch = super.read(); - if (ch >= 0) inPos += 1; - return ch; - } - public long skip(long n) throws IOException { - long ns = super.skip(n); - if (ns >= 0) inPos += ns; - return ns; - } - }); - } - - public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) { - this.attrDefs = attrDefs; - } - - public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) { - this.attrCommands = attrCommands; - } - - private void skip(int n, String what) throws IOException { - Utils.log.warning("skipping "+n+" bytes of "+what); - long skipped = 0; - while (skipped < n) { - long j = in.skip(n - skipped); - assert(j > 0); - skipped += j; - } - assert(skipped == n); - } - - private int readUnsignedShort() throws IOException { - return in.readUnsignedShort(); - } - - private int readInt() throws IOException { - return in.readInt(); - } - - /** Read a 2-byte int, and return the <em>global</em> CP entry for it. */ - private Entry readRef() throws IOException { - int i = in.readUnsignedShort(); - return i == 0 ? null : cls.cpMap[i]; - } - - private Entry readRef(byte tag) throws IOException { - Entry e = readRef(); - assert(!(e instanceof UnresolvedEntry)); - checkTag(e, tag); - return e; - } - - /** Throw a ClassFormatException if the entry does not match the expected tag type. */ - private Entry checkTag(Entry e, byte tag) throws ClassFormatException { - if (e == null || !e.tagMatches(tag)) { - String where = (inPos == constantPoolLimit - ? " in constant pool" - : " at pos: " + inPos); - String got = (e == null - ? "null CP index" - : "type=" + ConstantPool.tagName(e.tag)); - throw new ClassFormatException("Bad constant, expected type=" + - ConstantPool.tagName(tag) + - " got "+ got + ", in File: " + cls.file.nameString + where); - } - return e; - } - private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException { - return nullOK && e == null ? null : checkTag(e, tag); - } - - private Entry readRefOrNull(byte tag) throws IOException { - Entry e = readRef(); - checkTag(e, tag, true); - return e; - } - - private Utf8Entry readUtf8Ref() throws IOException { - return (Utf8Entry) readRef(CONSTANT_Utf8); - } - - private ClassEntry readClassRef() throws IOException { - return (ClassEntry) readRef(CONSTANT_Class); - } - - private ClassEntry readClassRefOrNull() throws IOException { - return (ClassEntry) readRefOrNull(CONSTANT_Class); - } - - private SignatureEntry readSignatureRef() throws IOException { - // The class file stores a Utf8, but we want a Signature. - Entry e = readRef(CONSTANT_Signature); - return (e != null && e.getTag() == CONSTANT_Utf8) - ? ConstantPool.getSignatureEntry(e.stringValue()) - : (SignatureEntry) e; - } - - void read() throws IOException { - boolean ok = false; - try { - readMagicNumbers(); - readConstantPool(); - readHeader(); - readMembers(false); // fields - readMembers(true); // methods - readAttributes(ATTR_CONTEXT_CLASS, cls); - fixUnresolvedEntries(); - cls.finishReading(); - assert(0 >= in.read(new byte[1])); - ok = true; - } finally { - if (!ok) { - if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file); - } - } - } - - void readMagicNumbers() throws IOException { - cls.magic = in.readInt(); - if (cls.magic != JAVA_MAGIC) - throw new Attribute.FormatException - ("Bad magic number in class file " - +Integer.toHexString(cls.magic), - ATTR_CONTEXT_CLASS, "magic-number", "pass"); - int minver = (short) readUnsignedShort(); - int majver = (short) readUnsignedShort(); - cls.version = Package.Version.of(majver, minver); - - //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver); - String bad = checkVersion(cls.version); - if (bad != null) { - throw new Attribute.FormatException - ("classfile version too "+bad+": " - +cls.version+" in "+cls.file, - ATTR_CONTEXT_CLASS, "version", "pass"); - } - } - - private String checkVersion(Package.Version ver) { - int majver = ver.major; - int minver = ver.minor; - if (majver < pkg.minClassVersion.major || - (majver == pkg.minClassVersion.major && - minver < pkg.minClassVersion.minor)) { - return "small"; - } - if (majver > pkg.maxClassVersion.major || - (majver == pkg.maxClassVersion.major && - minver > pkg.maxClassVersion.minor)) { - return "large"; - } - return null; // OK - } - - void readConstantPool() throws IOException { - int length = in.readUnsignedShort(); - //System.err.println("reading CP, length="+length); - - int[] fixups = new int[length*4]; - int fptr = 0; - - Entry[] cpMap = new Entry[length]; - cpMap[0] = null; - for (int i = 1; i < length; i++) { - //System.err.println("reading CP elt, i="+i); - int tag = in.readByte(); - switch (tag) { - case CONSTANT_Utf8: - cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF()); - break; - case CONSTANT_Integer: - { - cpMap[i] = ConstantPool.getLiteralEntry(in.readInt()); - } - break; - case CONSTANT_Float: - { - cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat()); - } - break; - case CONSTANT_Long: - { - cpMap[i] = ConstantPool.getLiteralEntry(in.readLong()); - cpMap[++i] = null; - } - break; - case CONSTANT_Double: - { - cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble()); - cpMap[++i] = null; - } - break; - - // just read the refs; do not attempt to resolve while reading - case CONSTANT_Class: - case CONSTANT_String: - case CONSTANT_MethodType: - fixups[fptr++] = i; - fixups[fptr++] = tag; - fixups[fptr++] = in.readUnsignedShort(); - fixups[fptr++] = -1; // empty ref2 - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - case CONSTANT_NameandType: - fixups[fptr++] = i; - fixups[fptr++] = tag; - fixups[fptr++] = in.readUnsignedShort(); - fixups[fptr++] = in.readUnsignedShort(); - break; - case CONSTANT_InvokeDynamic: - fixups[fptr++] = i; - fixups[fptr++] = tag; - fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref - fixups[fptr++] = in.readUnsignedShort(); - break; - case CONSTANT_MethodHandle: - fixups[fptr++] = i; - fixups[fptr++] = tag; - fixups[fptr++] = -1 ^ in.readUnsignedByte(); - fixups[fptr++] = in.readUnsignedShort(); - break; - default: - throw new ClassFormatException("Bad constant pool tag " + - tag + " in File: " + cls.file.nameString + - " at pos: " + inPos); - } - } - constantPoolLimit = inPos; - - // Fix up refs, which might be out of order. - while (fptr > 0) { - if (verbose > 3) - Utils.log.fine("CP fixups ["+fptr/4+"]"); - int flimit = fptr; - fptr = 0; - for (int fi = 0; fi < flimit; ) { - int cpi = fixups[fi++]; - int tag = fixups[fi++]; - int ref = fixups[fi++]; - int ref2 = fixups[fi++]; - if (verbose > 3) - Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}"); - if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) { - // Defer. - fixups[fptr++] = cpi; - fixups[fptr++] = tag; - fixups[fptr++] = ref; - fixups[fptr++] = ref2; - continue; - } - switch (tag) { - case CONSTANT_Class: - cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue()); - break; - case CONSTANT_String: - cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue()); - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - ClassEntry mclass = (ClassEntry) checkTag(cpMap[ref], CONSTANT_Class); - DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); - cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr); - break; - case CONSTANT_NameandType: - Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref], CONSTANT_Utf8); - Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature); - cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype); - break; - case CONSTANT_MethodType: - cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature)); - break; - case CONSTANT_MethodHandle: - byte refKind = (byte)(-1 ^ ref); - MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember); - cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef); - break; - case CONSTANT_InvokeDynamic: - DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); - cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr); - // Note that ref must be resolved later, using the BootstrapMethods attribute. - break; - default: - assert(false); - } - } - assert(fptr < flimit); // Must make progress. - } - - cls.cpMap = cpMap; - } - - private /*non-static*/ - class UnresolvedEntry extends Entry { - final Object[] refsOrIndexes; - UnresolvedEntry(byte tag, Object... refsOrIndexes) { - super(tag); - this.refsOrIndexes = refsOrIndexes; - ClassReader.this.haveUnresolvedEntry = true; - } - Entry resolve() { - Class cls = ClassReader.this.cls; - Entry res; - switch (tag) { - case CONSTANT_InvokeDynamic: - BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]); - DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1]; - res = ConstantPool.getInvokeDynamicEntry(iboots, idescr); - break; - default: - throw new AssertionError(); - } - return res; - } - private void unresolved() { throw new RuntimeException("unresolved entry has no string"); } - public int compareTo(Object x) { unresolved(); return 0; } - public boolean equals(Object x) { unresolved(); return false; } - protected int computeValueHash() { unresolved(); return 0; } - public String stringValue() { unresolved(); return toString(); } - public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; } - } - - boolean haveUnresolvedEntry; - private void fixUnresolvedEntries() { - if (!haveUnresolvedEntry) return; - Entry[] cpMap = cls.getCPMap(); - for (int i = 0; i < cpMap.length; i++) { - Entry e = cpMap[i]; - if (e instanceof UnresolvedEntry) { - cpMap[i] = e = ((UnresolvedEntry)e).resolve(); - assert(!(e instanceof UnresolvedEntry)); - } - } - haveUnresolvedEntry = false; - } - - void readHeader() throws IOException { - cls.flags = readUnsignedShort(); - cls.thisClass = readClassRef(); - cls.superClass = readClassRefOrNull(); - int ni = readUnsignedShort(); - cls.interfaces = new ClassEntry[ni]; - for (int i = 0; i < ni; i++) { - cls.interfaces[i] = readClassRef(); - } - } - - void readMembers(boolean doMethods) throws IOException { - int nm = readUnsignedShort(); - for (int i = 0; i < nm; i++) { - readMember(doMethods); - } - } - - void readMember(boolean doMethod) throws IOException { - int mflags = readUnsignedShort(); - Utf8Entry mname = readUtf8Ref(); - SignatureEntry mtype = readSignatureRef(); - DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype); - Class.Member m; - if (!doMethod) - m = cls.new Field(mflags, descr); - else - m = cls.new Method(mflags, descr); - readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD, - m); - } - void readAttributes(int ctype, Attribute.Holder h) throws IOException { - int na = readUnsignedShort(); - if (na == 0) return; // nothing to do here - if (verbose > 3) - Utils.log.fine("readAttributes "+h+" ["+na+"]"); - for (int i = 0; i < na; i++) { - String name = readUtf8Ref().stringValue(); - int length = readInt(); - // See if there is a special command that applies. - if (attrCommands != null) { - Attribute.Layout lkey = Attribute.keyForLookup(ctype, name); - String cmd = attrCommands.get(lkey); - if (cmd != null) { - switch (cmd) { - case "pass": - String message1 = "passing attribute bitwise in " + h; - throw new Attribute.FormatException(message1, ctype, name, cmd); - case "error": - String message2 = "attribute not allowed in " + h; - throw new Attribute.FormatException(message2, ctype, name, cmd); - case "strip": - skip(length, name + " attribute in " + h); - continue; - } - } - } - // Find canonical instance of the requested attribute. - Attribute a = Attribute.lookup(Package.attrDefs, ctype, name); - if (verbose > 4 && a != null) - Utils.log.fine("pkg_attribute_lookup "+name+" = "+a); - if (a == null) { - a = Attribute.lookup(this.attrDefs, ctype, name); - if (verbose > 4 && a != null) - Utils.log.fine("this "+name+" = "+a); - } - if (a == null) { - a = Attribute.lookup(null, ctype, name); - if (verbose > 4 && a != null) - Utils.log.fine("null_attribute_lookup "+name+" = "+a); - } - if (a == null && length == 0) { - // Any zero-length attr is "known"... - // We can assume an empty attr. has an empty layout. - // Handles markers like Enum, Bridge, Synthetic, Deprecated. - a = Attribute.find(ctype, name, ""); - } - boolean isStackMap = (ctype == ATTR_CONTEXT_CODE - && (name.equals("StackMap") || - name.equals("StackMapX"))); - if (isStackMap) { - // Known attribute but with a corner case format, "pass" it. - Code code = (Code) h; - final int TOO_BIG = 0x10000; - if (code.max_stack >= TOO_BIG || - code.max_locals >= TOO_BIG || - code.getLength() >= TOO_BIG || - name.endsWith("X")) { - // No, we don't really know what to do with this one. - // Do not compress the rare and strange "u4" and "X" cases. - a = null; - } - } - if (a == null) { - if (isStackMap) { - // Known attribute but w/o a format; pass it. - String message = "unsupported StackMap variant in "+h; - throw new Attribute.FormatException(message, ctype, name, - "pass"); - } else if ("strip".equals(unknownAttrCommand)) { - // Skip the unknown attribute. - skip(length, "unknown "+name+" attribute in "+h); - continue; - } else { - String message = " is unknown attribute in class " + h; - throw new Attribute.FormatException(message, ctype, name, - unknownAttrCommand); - } - } - long pos0 = inPos; // in case we want to check it - if (a.layout() == Package.attrCodeEmpty) { - // These are hardwired. - Class.Method m = (Class.Method) h; - m.code = new Code(m); - try { - readCode(m.code); - } catch (Instruction.FormatException iie) { - String message = iie.getMessage() + " in " + h; - throw new ClassReader.ClassFormatException(message, iie); - } - assert(length == inPos - pos0); - // Keep empty attribute a... - } else if (a.layout() == Package.attrBootstrapMethodsEmpty) { - assert(h == cls); - readBootstrapMethods(cls); - assert(length == inPos - pos0); - // Delete the attribute; it is logically part of the constant pool. - continue; - } else if (a.layout() == Package.attrInnerClassesEmpty) { - // These are hardwired also. - assert(h == cls); - readInnerClasses(cls); - assert(length == inPos - pos0); - // Keep empty attribute a... - } else if (length > 0) { - byte[] bytes = new byte[length]; - in.readFully(bytes); - a = a.addContent(bytes); - } - if (a.size() == 0 && !a.layout().isEmpty()) { - throw new ClassFormatException(name + - ": attribute length cannot be zero, in " + h); - } - h.addAttribute(a); - if (verbose > 2) - Utils.log.fine("read "+a); - } - } - - void readCode(Code code) throws IOException { - code.max_stack = readUnsignedShort(); - code.max_locals = readUnsignedShort(); - code.bytes = new byte[readInt()]; - in.readFully(code.bytes); - Entry[] cpMap = cls.getCPMap(); - Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version); - int nh = readUnsignedShort(); - code.setHandlerCount(nh); - for (int i = 0; i < nh; i++) { - code.handler_start[i] = readUnsignedShort(); - code.handler_end[i] = readUnsignedShort(); - code.handler_catch[i] = readUnsignedShort(); - code.handler_class[i] = readClassRefOrNull(); - } - readAttributes(ATTR_CONTEXT_CODE, code); - } - - void readBootstrapMethods(Class cls) throws IOException { - BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()]; - for (int i = 0; i < bsms.length; i++) { - MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle); - Entry[] argRefs = new Entry[readUnsignedShort()]; - for (int j = 0; j < argRefs.length; j++) { - argRefs[j] = readRef(CONSTANT_LoadableValue); - } - bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs); - } - cls.setBootstrapMethods(Arrays.asList(bsms)); - } - - void readInnerClasses(Class cls) throws IOException { - int nc = readUnsignedShort(); - ArrayList<InnerClass> ics = new ArrayList<>(nc); - for (int i = 0; i < nc; i++) { - InnerClass ic = - new InnerClass(readClassRef(), - readClassRefOrNull(), - (Utf8Entry)readRefOrNull(CONSTANT_Utf8), - readUnsignedShort()); - ics.add(ic); - } - cls.innerClasses = ics; // set directly; do not use setInnerClasses. - // (Later, ics may be transferred to the pkg.) - } - - static class ClassFormatException extends IOException { - @java.io.Serial - private static final long serialVersionUID = -3564121733989501833L; - - public ClassFormatException(String message) { - super(message); - } - - public ClassFormatException(String message, Throwable cause) { - super(message, cause); - } - } -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/ClassWriter.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,317 +0,0 @@ -/* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - - -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.Index; -import com.sun.java.util.jar.pack.ConstantPool.NumberEntry; -import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; -import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; -import com.sun.java.util.jar.pack.Package.Class; -import com.sun.java.util.jar.pack.Package.InnerClass; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import static com.sun.java.util.jar.pack.Constants.*; -/** - * Writer for a class file that is incorporated into a package. - * @author John Rose - */ -class ClassWriter { - int verbose; - - Package pkg; - Class cls; - DataOutputStream out; - Index cpIndex; - Index bsmIndex; - - ClassWriter(Class cls, OutputStream out) throws IOException { - this.pkg = cls.getPackage(); - this.cls = cls; - this.verbose = pkg.verbose; - this.out = new DataOutputStream(new BufferedOutputStream(out)); - this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap()); - this.cpIndex.flattenSigs = true; - if (cls.hasBootstrapMethods()) { - this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods", - cls.getBootstrapMethodMap()); - } - if (verbose > 1) - Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString())); - } - - private void writeShort(int x) throws IOException { - out.writeShort(x); - } - - private void writeInt(int x) throws IOException { - out.writeInt(x); - } - - /** Write a 2-byte int representing a CP entry, using the local cpIndex. */ - private void writeRef(Entry e) throws IOException { - writeRef(e, cpIndex); - } - - /** Write a 2-byte int representing a CP entry, using the given cpIndex. */ - private void writeRef(Entry e, Index cpIndex) throws IOException { - int i = (e == null) ? 0 : cpIndex.indexOf(e); - writeShort(i); - } - - void write() throws IOException { - boolean ok = false; - try { - if (verbose > 1) Utils.log.fine("...writing "+cls); - writeMagicNumbers(); - writeConstantPool(); - writeHeader(); - writeMembers(false); // fields - writeMembers(true); // methods - writeAttributes(ATTR_CONTEXT_CLASS, cls); - /* Closing here will cause all the underlying - streams to close, Causing the jar stream - to close prematurely, instead we just flush. - out.close(); - */ - out.flush(); - ok = true; - } finally { - if (!ok) { - Utils.log.warning("Error on output of "+cls); - } - } - } - - void writeMagicNumbers() throws IOException { - writeInt(cls.magic); - writeShort(cls.version.minor); - writeShort(cls.version.major); - } - - void writeConstantPool() throws IOException { - Entry[] cpMap = cls.cpMap; - writeShort(cpMap.length); - for (int i = 0; i < cpMap.length; i++) { - Entry e = cpMap[i]; - assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord())); - if (e == null) continue; - byte tag = e.getTag(); - if (verbose > 2) Utils.log.fine(" CP["+i+"] = "+e); - out.write(tag); - switch (tag) { - case CONSTANT_Signature: - throw new AssertionError("CP should have Signatures remapped to Utf8"); - case CONSTANT_Utf8: - out.writeUTF(e.stringValue()); - break; - case CONSTANT_Integer: - out.writeInt(((NumberEntry)e).numberValue().intValue()); - break; - case CONSTANT_Float: - float fval = ((NumberEntry)e).numberValue().floatValue(); - out.writeInt(Float.floatToRawIntBits(fval)); - break; - case CONSTANT_Long: - out.writeLong(((NumberEntry)e).numberValue().longValue()); - break; - case CONSTANT_Double: - double dval = ((NumberEntry)e).numberValue().doubleValue(); - out.writeLong(Double.doubleToRawLongBits(dval)); - break; - case CONSTANT_Class: - case CONSTANT_String: - case CONSTANT_MethodType: - writeRef(e.getRef(0)); - break; - case CONSTANT_MethodHandle: - MethodHandleEntry mhe = (MethodHandleEntry) e; - out.writeByte(mhe.refKind); - writeRef(mhe.getRef(0)); - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - case CONSTANT_NameandType: - writeRef(e.getRef(0)); - writeRef(e.getRef(1)); - break; - case CONSTANT_InvokeDynamic: - writeRef(e.getRef(0), bsmIndex); - writeRef(e.getRef(1)); - break; - case CONSTANT_BootstrapMethod: - throw new AssertionError("CP should have BootstrapMethods moved to side-table"); - default: - throw new IOException("Bad constant pool tag "+tag); - } - } - } - - void writeHeader() throws IOException { - writeShort(cls.flags); - writeRef(cls.thisClass); - writeRef(cls.superClass); - writeShort(cls.interfaces.length); - for (int i = 0; i < cls.interfaces.length; i++) { - writeRef(cls.interfaces[i]); - } - } - - void writeMembers(boolean doMethods) throws IOException { - List<? extends Class.Member> mems; - if (!doMethods) - mems = cls.getFields(); - else - mems = cls.getMethods(); - writeShort(mems.size()); - for (Class.Member m : mems) { - writeMember(m, doMethods); - } - } - - void writeMember(Class.Member m, boolean doMethod) throws IOException { - if (verbose > 2) Utils.log.fine("writeMember "+m); - writeShort(m.flags); - writeRef(m.getDescriptor().nameRef); - writeRef(m.getDescriptor().typeRef); - writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD, - m); - } - - private void reorderBSMandICS(Attribute.Holder h) { - Attribute bsmAttr = h.getAttribute(Package.attrBootstrapMethodsEmpty); - if (bsmAttr == null) return; - - Attribute icsAttr = h.getAttribute(Package.attrInnerClassesEmpty); - if (icsAttr == null) return; - - int bsmidx = h.attributes.indexOf(bsmAttr); - int icsidx = h.attributes.indexOf(icsAttr); - if (bsmidx > icsidx) { - h.attributes.remove(bsmAttr); - h.attributes.add(icsidx, bsmAttr); - } - return; - } - - // handy buffer for collecting attrs - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream bufOut = new DataOutputStream(buf); - - void writeAttributes(int ctype, Attribute.Holder h) throws IOException { - if (h.attributes == null) { - writeShort(0); // attribute size - return; - } - // there may be cases if an InnerClass attribute is explicit, then the - // ordering could be wrong, fix the ordering before we write it out. - if (h instanceof Package.Class) - reorderBSMandICS(h); - - writeShort(h.attributes.size()); - for (Attribute a : h.attributes) { - a.finishRefs(cpIndex); - writeRef(a.getNameRef()); - if (a.layout() == Package.attrCodeEmpty || - a.layout() == Package.attrBootstrapMethodsEmpty || - a.layout() == Package.attrInnerClassesEmpty) { - // These are hardwired. - DataOutputStream savedOut = out; - assert(out != bufOut); - buf.reset(); - out = bufOut; - if ("Code".equals(a.name())) { - Class.Method m = (Class.Method) h; - writeCode(m.code); - } else if ("BootstrapMethods".equals(a.name())) { - assert(h == cls); - writeBootstrapMethods(cls); - } else if ("InnerClasses".equals(a.name())) { - assert(h == cls); - writeInnerClasses(cls); - } else { - throw new AssertionError(); - } - out = savedOut; - if (verbose > 2) - Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]"); - writeInt(buf.size()); - buf.writeTo(out); - } else { - if (verbose > 2) - Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]"); - writeInt(a.size()); - out.write(a.bytes()); - } - } - } - - void writeCode(Code code) throws IOException { - code.finishRefs(cpIndex); - writeShort(code.max_stack); - writeShort(code.max_locals); - writeInt(code.bytes.length); - out.write(code.bytes); - int nh = code.getHandlerCount(); - writeShort(nh); - for (int i = 0; i < nh; i++) { - writeShort(code.handler_start[i]); - writeShort(code.handler_end[i]); - writeShort(code.handler_catch[i]); - writeRef(code.handler_class[i]); - } - writeAttributes(ATTR_CONTEXT_CODE, code); - } - - void writeBootstrapMethods(Class cls) throws IOException { - List<BootstrapMethodEntry> bsms = cls.getBootstrapMethods(); - writeShort(bsms.size()); - for (BootstrapMethodEntry e : bsms) { - writeRef(e.bsmRef); - writeShort(e.argRefs.length); - for (Entry argRef : e.argRefs) { - writeRef(argRef); - } - } - } - - void writeInnerClasses(Class cls) throws IOException { - List<InnerClass> ics = cls.getInnerClasses(); - writeShort(ics.size()); - for (InnerClass ic : ics) { - writeRef(ic.thisClass); - writeRef(ic.outerClass); - writeRef(ic.name); - writeShort(ic.flags); - } - } -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/Code.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import com.sun.java.util.jar.pack.Package.Class; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.Collection; -import static com.sun.java.util.jar.pack.Constants.*; - -/** - * Represents a chunk of bytecodes. - * @author John Rose - */ -class Code extends Attribute.Holder { - Class.Method m; - - public Code(Class.Method m) { - this.m = m; - } - - public Class.Method getMethod() { - return m; - } - public Class thisClass() { - return m.thisClass(); - } - public Package getPackage() { - return m.thisClass().getPackage(); - } - - public ConstantPool.Entry[] getCPMap() { - return m.getCPMap(); - } - - private static final ConstantPool.Entry[] noRefs = ConstantPool.noRefs; - - // The following fields are used directly by the ClassReader, etc. - int max_stack; - int max_locals; - - ConstantPool.Entry handler_class[] = noRefs; - int handler_start[] = noInts; - int handler_end[] = noInts; - int handler_catch[] = noInts; - - byte[] bytes; - Fixups fixups; // reference relocations, if any are required - Object insnMap; // array of instruction boundaries - - int getLength() { return bytes.length; } - - int getMaxStack() { - return max_stack; - } - void setMaxStack(int ms) { - max_stack = ms; - } - - int getMaxNALocals() { - int argsize = m.getArgumentSize(); - return max_locals - argsize; - } - void setMaxNALocals(int ml) { - int argsize = m.getArgumentSize(); - max_locals = argsize + ml; - } - - int getHandlerCount() { - assert(handler_class.length == handler_start.length); - assert(handler_class.length == handler_end.length); - assert(handler_class.length == handler_catch.length); - return handler_class.length; - } - void setHandlerCount(int h) { - if (h > 0) { - handler_class = new ConstantPool.Entry[h]; - handler_start = new int[h]; - handler_end = new int[h]; - handler_catch = new int[h]; - // caller must fill these in ASAP - } - } - - void setBytes(byte[] bytes) { - this.bytes = bytes; - if (fixups != null) - fixups.setBytes(bytes); - } - - void setInstructionMap(int[] insnMap, int mapLen) { - //int[] oldMap = null; - //assert((oldMap = getInstructionMap()) != null); - this.insnMap = allocateInstructionMap(insnMap, mapLen); - //assert(Arrays.equals(oldMap, getInstructionMap())); - } - void setInstructionMap(int[] insnMap) { - setInstructionMap(insnMap, insnMap.length); - } - - int[] getInstructionMap() { - return expandInstructionMap(getInsnMap()); - } - - void addFixups(Collection<Fixups.Fixup> moreFixups) { - if (fixups == null) { - fixups = new Fixups(bytes); - } - assert(fixups.getBytes() == bytes); - fixups.addAll(moreFixups); - } - - public void trimToSize() { - if (fixups != null) { - fixups.trimToSize(); - if (fixups.size() == 0) - fixups = null; - } - super.trimToSize(); - } - - protected void visitRefs(int mode, Collection<ConstantPool.Entry> refs) { - int verbose = getPackage().verbose; - if (verbose > 2) - System.out.println("Reference scan "+this); - refs.addAll(Arrays.asList(handler_class)); - if (fixups != null) { - fixups.visitRefs(refs); - } else { - // References (to a local cpMap) are embedded in the bytes. - ConstantPool.Entry[] cpMap = getCPMap(); - for (Instruction i = instructionAt(0); i != null; i = i.next()) { - if (verbose > 4) - System.out.println(i); - int cpref = i.getCPIndex(); - if (cpref >= 0) { - refs.add(cpMap[cpref]); - } - } - } - // Handle attribute list: - super.visitRefs(mode, refs); - } - - // Since bytecodes are the single largest contributor to - // package size, it's worth a little bit of trouble - // to reduce the per-bytecode memory footprint. - // In the current scheme, half of the bulk of these arrays - // due to bytes, and half to shorts. (Ints are insignificant.) - // Given an average of 1.8 bytes per instruction, this means - // instruction boundary arrays are about a 75% overhead--tolerable. - // (By using bytes, we get 33% savings over just shorts and ints. - // Using both bytes and shorts gives 66% savings over just ints.) - static final boolean shrinkMaps = true; - - private Object allocateInstructionMap(int[] insnMap, int mapLen) { - int PClimit = getLength(); - if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) { - byte[] map = new byte[mapLen+1]; - for (int i = 0; i < mapLen; i++) { - map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE); - } - map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE); - return map; - } else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) { - short[] map = new short[mapLen+1]; - for (int i = 0; i < mapLen; i++) { - map[i] = (short)(insnMap[i] + Short.MIN_VALUE); - } - map[mapLen] = (short)(PClimit + Short.MIN_VALUE); - return map; - } else { - int[] map = Arrays.copyOf(insnMap, mapLen + 1); - map[mapLen] = PClimit; - return map; - } - } - private int[] expandInstructionMap(Object map0) { - int[] imap; - if (map0 instanceof byte[]) { - byte[] map = (byte[]) map0; - imap = new int[map.length-1]; - for (int i = 0; i < imap.length; i++) { - imap[i] = map[i] - Byte.MIN_VALUE; - } - } else if (map0 instanceof short[]) { - short[] map = (short[]) map0; - imap = new int[map.length-1]; - for (int i = 0; i < imap.length; i++) { - imap[i] = map[i] - Byte.MIN_VALUE; - } - } else { - int[] map = (int[]) map0; - imap = Arrays.copyOfRange(map, 0, map.length - 1); - } - return imap; - } - - Object getInsnMap() { - // Build a map of instruction boundaries. - if (insnMap != null) { - return insnMap; - } - int[] map = new int[getLength()]; - int fillp = 0; - for (Instruction i = instructionAt(0); i != null; i = i.next()) { - map[fillp++] = i.getPC(); - } - // Make it byte[], short[], or int[] according to the max BCI. - insnMap = allocateInstructionMap(map, fillp); - //assert(assertBCICodingsOK()); - return insnMap; - } - - /** Encode the given BCI as an instruction boundary number. - * For completeness, irregular (non-boundary) BCIs are - * encoded compactly immediately after the boundary numbers. - * This encoding is the identity mapping outside 0..length, - * and it is 1-1 everywhere. All by itself this technique - * improved zipped rt.jar compression by 2.6%. - */ - public int encodeBCI(int bci) { - if (bci <= 0 || bci > getLength()) return bci; - Object map0 = getInsnMap(); - int i, len; - if (shrinkMaps && map0 instanceof byte[]) { - byte[] map = (byte[]) map0; - len = map.length; - i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE)); - } else if (shrinkMaps && map0 instanceof short[]) { - short[] map = (short[]) map0; - len = map.length; - i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE)); - } else { - int[] map = (int[]) map0; - len = map.length; - i = Arrays.binarySearch(map, bci); - } - assert(i != -1); - assert(i != 0); - assert(i != len); - assert(i != -len-1); - return (i >= 0) ? i : len + bci - (-i-1); - } - public int decodeBCI(int bciCode) { - if (bciCode <= 0 || bciCode > getLength()) return bciCode; - Object map0 = getInsnMap(); - int i, len; - // len == map.length - // If bciCode < len, result is map[bciCode], the common and fast case. - // Otherwise, let map[i] be the smallest map[*] larger than bci. - // Then, required by the return statement of encodeBCI: - // bciCode == len + bci - i - // Thus: - // bci-i == bciCode-len - // map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1]) - // We can solve this by searching for adjacent entries - // map[i-1], map[i] such that: - // map[i-1]-(i-1) <= bciCode-len < map[i]-i - // This can be approximated by searching map[i] for bciCode and then - // linear searching backward. Given the right i, we then have: - // bci == bciCode-len + i - // This linear search is at its worst case for indexes in the beginning - // of a large method, but it's not clear that this is a problem in - // practice, since BCIs are usually on instruction boundaries. - if (shrinkMaps && map0 instanceof byte[]) { - byte[] map = (byte[]) map0; - len = map.length; - if (bciCode < len) - return map[bciCode] - Byte.MIN_VALUE; - i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE)); - if (i < 0) i = -i-1; - int key = bciCode-len + Byte.MIN_VALUE; - for (;; i--) { - if (map[i-1]-(i-1) <= key) break; - } - } else if (shrinkMaps && map0 instanceof short[]) { - short[] map = (short[]) map0; - len = map.length; - if (bciCode < len) - return map[bciCode] - Short.MIN_VALUE; - i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE)); - if (i < 0) i = -i-1; - int key = bciCode-len + Short.MIN_VALUE; - for (;; i--) { - if (map[i-1]-(i-1) <= key) break; - } - } else { - int[] map = (int[]) map0; - len = map.length; - if (bciCode < len) - return map[bciCode]; - i = Arrays.binarySearch(map, bciCode); - if (i < 0) i = -i-1; - int key = bciCode-len; - for (;; i--) { - if (map[i-1]-(i-1) <= key) break; - } - } - return bciCode-len + i; - } - - public void finishRefs(ConstantPool.Index ix) { - if (fixups != null) { - fixups.finishRefs(ix); - fixups = null; - } - // Code attributes are finished in ClassWriter.writeAttributes. - } - - Instruction instructionAt(int pc) { - return Instruction.at(bytes, pc); - } - - static boolean flagsRequireCode(int flags) { - // A method's flags force it to have a Code attribute, - // if the flags are neither native nor abstract. - return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0; - } - - public String toString() { - return m+".Code"; - } - - /// Fetching values from my own array. - public int getInt(int pc) { return Instruction.getInt(bytes, pc); } - public int getShort(int pc) { return Instruction.getShort(bytes, pc); } - public int getByte(int pc) { return Instruction.getByte(bytes, pc); } - void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); } - void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); } - void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); } - -/* TEST CODE ONLY - private boolean assertBCICodingsOK() { - boolean ok = true; - int len = java.lang.reflect.Array.getLength(insnMap); - int base = 0; - if (insnMap.getClass().getComponentType() == Byte.TYPE) - base = Byte.MIN_VALUE; - if (insnMap.getClass().getComponentType() == Short.TYPE) - base = Short.MIN_VALUE; - for (int i = -1, imax = getLength()+1; i <= imax; i++) { - int bci = i; - int enc = Math.min(-999, bci-1); - int dec = enc; - try { - enc = encodeBCI(bci); - dec = decodeBCI(enc); - } catch (RuntimeException ee) { - ee.printStackTrace(); - } - if (dec == bci) { - //System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc); - continue; - } - if (ok) { - for (int q = 0; q <= 1; q++) { - StringBuffer sb = new StringBuffer(); - sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {"); - for (int j = 0; j < len; j++) { - int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base; - mapi -= j*q; - sb.append(" "+mapi); - } - sb.append(" }"); - System.out.println("*** "+sb); - } - } - System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec); - ok = false; - } - return ok; - } -//*/ -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/Coding.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,906 +0,0 @@ -/* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; -import static com.sun.java.util.jar.pack.Constants.*; -/** - * Define the conversions between sequences of small integers and raw bytes. - * This is a schema of encodings which incorporates varying lengths, - * varying degrees of length variability, and varying amounts of signed-ness. - * @author John Rose - */ -class Coding implements Comparable<Coding>, CodingMethod, Histogram.BitMetric { - /* - Coding schema for single integers, parameterized by (B,H,S): - - Let B in [1,5], H in [1,256], S in [0,3]. - (S limit is arbitrary. B follows the 32-bit limit. H is byte size.) - - A given (B,H,S) code varies in length from 1 to B bytes. - - The 256 values a byte may take on are divided into L=(256-H) and H - values, with all the H values larger than the L values. - (That is, the L values are [0,L) and the H are [L,256).) - - The last byte is always either the B-th byte, a byte with "L value" - (<L), or both. There is no other byte that satisfies these conditions. - All bytes before the last always have "H values" (>=L). - - Therefore, if L==0, the code always has the full length of B bytes. - The coding then becomes a classic B-byte little-endian unsigned integer. - (Also, if L==128, the high bit of each byte acts signals the presence - of a following byte, up to the maximum length.) - - In the unsigned case (S==0), the coding is compact and monotonic - in the ordering of byte sequences defined by appending zero bytes - to pad them to a common length B, reversing them, and ordering them - lexicographically. (This agrees with "little-endian" byte order.) - - Therefore, the unsigned value of a byte sequence may be defined as: - <pre> - U(b0) == b0 - in [0..L) - or [0..256) if B==1 (**) - - U(b0,b1) == b0 + b1*H - in [L..L*(1+H)) - or [L..L*(1+H) + H^2) if B==2 - - U(b0,b1,b2) == b0 + b1*H + b2*H^2 - in [L*(1+H)..L*(1+H+H^2)) - or [L*(1+H)..L*(1+H+H^2) + H^3) if B==3 - - U(b[i]: i<n) == Sum[i<n]( b[i] * H^i ) - up to L*Sum[i<n]( H^i ) - or to L*Sum[i<n]( H^i ) + H^n if n==B - </pre> - - (**) If B==1, the values H,L play no role in the coding. - As a convention, we require that any (1,H,S) code must always - encode values less than H. Thus, a simple unsigned byte is coded - specifically by the code (1,256,0). - - (Properly speaking, the unsigned case should be parameterized as - S==Infinity. If the schema were regular, the case S==0 would really - denote a numbering in which all coded values are negative.) - - If S>0, the unsigned value of a byte sequence is regarded as a binary - integer. If any of the S low-order bits are zero, the corresponding - signed value will be non-negative. If all of the S low-order bits - (S>0) are one, the corresponding signed value will be negative. - - The non-negative signed values are compact and monotonically increasing - (from 0) in the ordering of the corresponding unsigned values. - - The negative signed values are compact and monotonically decreasing - (from -1) in the ordering of the corresponding unsigned values. - - In essence, the low-order S bits function as a collective sign bit - for negative signed numbers, and as a low-order base-(2^S-1) digit - for non-negative signed numbers. - - Therefore, the signed value corresponding to an unsigned value is: - <pre> - Sgn(x) == x if S==0 - Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S), if S>0, (x % 2^S) < 2^S-1 - Sgn(x) == -(x / 2^S)-1, if S>0, (x % 2^S) == 2^S-1 - </pre> - - Finally, the value of a byte sequence, given the coding parameters - (B,H,S), is defined as: - <pre> - V(b[i]: i<n) == Sgn(U(b[i]: i<n)) - </pre> - - The extremal positive and negative signed value for a given range - of unsigned values may be found by sign-encoding the largest unsigned - value which is not 2^S-1 mod 2^S, and that which is, respectively. - - Because B,H,S are variable, this is not a single coding but a schema - of codings. For optimal compression, it is necessary to adaptively - select specific codings to the data being compressed. - - For example, if a sequence of values happens never to be negative, - S==0 is the best choice. If the values are equally balanced between - negative and positive, S==1. If negative values are rare, then S>1 - is more appropriate. - - A (B,H,S) encoding is called a "subrange" if it does not encode - the largest 32-bit value, and if the number R of values it does - encode can be expressed as a positive 32-bit value. (Note that - B=1 implies R<=256, B=2 implies R<=65536, etc.) - - A delta version of a given (B,H,S) coding encodes an array of integers - by writing their successive differences in the (B,H,S) coding. - The original integers themselves may be recovered by making a - running accumulation of sum of the differences as they are read. - - As a special case, if a (B,H,S) encoding is a subrange, its delta - version will only encode arrays of numbers in the coding's unsigned - range, [0..R-1]. The coding of deltas is still in the normal signed - range, if S!=0. During delta encoding, all subtraction results are - reduced to the signed range, by adding multiples of R. Likewise, -. during encoding, all addition results are reduced to the unsigned range. - This special case for subranges allows the benefits of wraparound - when encoding correlated sequences of very small positive numbers. - */ - - // Code-specific limits: - private static int saturate32(long x) { - if (x > Integer.MAX_VALUE) return Integer.MAX_VALUE; - if (x < Integer.MIN_VALUE) return Integer.MIN_VALUE; - return (int)x; - } - private static long codeRangeLong(int B, int H) { - return codeRangeLong(B, H, B); - } - private static long codeRangeLong(int B, int H, int nMax) { - // Code range for a all (B,H) codes of length <=nMax (<=B). - // n < B: L*Sum[i<n]( H^i ) - // n == B: L*Sum[i<B]( H^i ) + H^B - assert(nMax >= 0 && nMax <= B); - assert(B >= 1 && B <= 5); - assert(H >= 1 && H <= 256); - if (nMax == 0) return 0; // no codes of zero length - if (B == 1) return H; // special case; see (**) above - int L = 256-H; - long sum = 0; - long H_i = 1; - for (int n = 1; n <= nMax; n++) { - sum += H_i; - H_i *= H; - } - sum *= L; - if (nMax == B) - sum += H_i; - return sum; - } - /** Largest int representable by (B,H,S) in up to nMax bytes. */ - public static int codeMax(int B, int H, int S, int nMax) { - //assert(S >= 0 && S <= S_MAX); - long range = codeRangeLong(B, H, nMax); - if (range == 0) - return -1; // degenerate max value for empty set of codes - if (S == 0 || range >= (long)1<<32) - return saturate32(range-1); - long maxPos = range-1; - while (isNegativeCode(maxPos, S)) { - --maxPos; - } - if (maxPos < 0) return -1; // No positive codings at all. - int smax = decodeSign32(maxPos, S); - // check for 32-bit wraparound: - if (smax < 0) - return Integer.MAX_VALUE; - return smax; - } - /** Smallest int representable by (B,H,S) in up to nMax bytes. - Returns Integer.MIN_VALUE if 32-bit wraparound covers - the entire negative range. - */ - public static int codeMin(int B, int H, int S, int nMax) { - //assert(S >= 0 && S <= S_MAX); - long range = codeRangeLong(B, H, nMax); - if (range >= (long)1<<32 && nMax == B) { - // Can code negative values via 32-bit wraparound. - return Integer.MIN_VALUE; - } - if (S == 0) { - return 0; - } - long maxNeg = range-1; - while (!isNegativeCode(maxNeg, S)) - --maxNeg; - - if (maxNeg < 0) return 0; // No negative codings at all. - return decodeSign32(maxNeg, S); - } - - // Some of the arithmetic below is on unsigned 32-bit integers. - // These must be represented in Java as longs in the range [0..2^32-1]. - // The conversion to a signed int is just the Java cast (int), but - // the conversion to an unsigned int is the following little method: - private static long toUnsigned32(int sx) { - return ((long)sx << 32) >>> 32; - } - - // Sign encoding: - private static boolean isNegativeCode(long ux, int S) { - assert(S > 0); - assert(ux >= -1); // can be out of 32-bit range; who cares - int Smask = (1<<S)-1; - return (((int)ux+1) & Smask) == 0; - } - private static boolean hasNegativeCode(int sx, int S) { - assert(S > 0); - // If S>=2 very low negatives are coded by 32-bit-wrapped positives. - // The lowest negative representable by a negative coding is - // ~(umax32 >> S), and the next lower number is coded by wrapping - // the highest positive: - // CodePos(umax32-1) -> (umax32-1)-((umax32-1)>>S) - // which simplifies to ~(umax32 >> S)-1. - return (0 > sx) && (sx >= ~(-1>>>S)); - } - private static int decodeSign32(long ux, int S) { - assert(ux == toUnsigned32((int)ux)) // must be unsigned 32-bit number - : (Long.toHexString(ux)); - if (S == 0) { - return (int) ux; // cast to signed int - } - int sx; - if (isNegativeCode(ux, S)) { - // Sgn(x) == -(x / 2^S)-1 - sx = ~((int)ux >>> S); - } else { - // Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S) - sx = (int)ux - ((int)ux >>> S); - } - // Assert special case of S==1: - assert(!(S == 1) || sx == (((int)ux >>> 1) ^ -((int)ux & 1))); - return sx; - } - private static long encodeSign32(int sx, int S) { - if (S == 0) { - return toUnsigned32(sx); // unsigned 32-bit int - } - int Smask = (1<<S)-1; - long ux; - if (!hasNegativeCode(sx, S)) { - // InvSgn(sx) = (sx / (2^S-1))*2^S + (sx % (2^S-1)) - ux = sx + (toUnsigned32(sx) / Smask); - } else { - // InvSgn(sx) = (-sx-1)*2^S + (2^S-1) - ux = (-sx << S) - 1; - } - ux = toUnsigned32((int)ux); - assert(sx == decodeSign32(ux, S)) - : (Long.toHexString(ux)+" -> "+ - Integer.toHexString(sx)+" != "+ - Integer.toHexString(decodeSign32(ux, S))); - return ux; - } - - // Top-level coding of single integers: - public static void writeInt(byte[] out, int[] outpos, int sx, int B, int H, int S) { - long ux = encodeSign32(sx, S); - assert(ux == toUnsigned32((int)ux)); - assert(ux < codeRangeLong(B, H)) - : Long.toHexString(ux); - int L = 256-H; - long sum = ux; - int pos = outpos[0]; - for (int i = 0; i < B-1; i++) { - if (sum < L) - break; - sum -= L; - int b_i = (int)( L + (sum % H) ); - sum /= H; - out[pos++] = (byte)b_i; - } - out[pos++] = (byte)sum; - // Report number of bytes written by updating outpos[0]: - outpos[0] = pos; - // Check right away for mis-coding. - //assert(sx == readInt(out, new int[1], B, H, S)); - } - public static int readInt(byte[] in, int[] inpos, int B, int H, int S) { - // U(b[i]: i<n) == Sum[i<n]( b[i] * H^i ) - int L = 256-H; - long sum = 0; - long H_i = 1; - int pos = inpos[0]; - for (int i = 0; i < B; i++) { - int b_i = in[pos++] & 0xFF; - sum += b_i*H_i; - H_i *= H; - if (b_i < L) break; - } - //assert(sum >= 0 && sum < codeRangeLong(B, H)); - // Report number of bytes read by updating inpos[0]: - inpos[0] = pos; - return decodeSign32(sum, S); - } - // The Stream version doesn't fetch a byte unless it is needed for coding. - public static int readIntFrom(InputStream in, int B, int H, int S) throws IOException { - // U(b[i]: i<n) == Sum[i<n]( b[i] * H^i ) - int L = 256-H; - long sum = 0; - long H_i = 1; - for (int i = 0; i < B; i++) { - int b_i = in.read(); - if (b_i < 0) throw new RuntimeException("unexpected EOF"); - sum += b_i*H_i; - H_i *= H; - if (b_i < L) break; - } - assert(sum >= 0 && sum < codeRangeLong(B, H)); - return decodeSign32(sum, S); - } - - public static final int B_MAX = 5; /* B: [1,5] */ - public static final int H_MAX = 256; /* H: [1,256] */ - public static final int S_MAX = 2; /* S: [0,2] */ - - // END OF STATICS. - - private final int B; /*1..5*/ // # bytes (1..5) - private final int H; /*1..256*/ // # codes requiring a higher byte - private final int L; /*0..255*/ // # codes requiring a higher byte - private final int S; /*0..3*/ // # low-order bits representing sign - private final int del; /*0..2*/ // type of delta encoding (0 == none) - private final int min; // smallest representable value - private final int max; // largest representable value - private final int umin; // smallest representable uns. value - private final int umax; // largest representable uns. value - private final int[] byteMin; // smallest repr. value, given # bytes - private final int[] byteMax; // largest repr. value, given # bytes - - private Coding(int B, int H, int S) { - this(B, H, S, 0); - } - private Coding(int B, int H, int S, int del) { - this.B = B; - this.H = H; - this.L = 256-H; - this.S = S; - this.del = del; - this.min = codeMin(B, H, S, B); - this.max = codeMax(B, H, S, B); - this.umin = codeMin(B, H, 0, B); - this.umax = codeMax(B, H, 0, B); - this.byteMin = new int[B]; - this.byteMax = new int[B]; - - for (int nMax = 1; nMax <= B; nMax++) { - byteMin[nMax-1] = codeMin(B, H, S, nMax); - byteMax[nMax-1] = codeMax(B, H, S, nMax); - } - } - - public boolean equals(Object x) { - if (!(x instanceof Coding)) return false; - Coding that = (Coding) x; - if (this.B != that.B) return false; - if (this.H != that.H) return false; - if (this.S != that.S) return false; - if (this.del != that.del) return false; - return true; - } - - public int hashCode() { - return (del<<14)+(S<<11)+(B<<8)+(H<<0); - } - - private static Map<Coding, Coding> codeMap; - - private static synchronized Coding of(int B, int H, int S, int del) { - if (codeMap == null) codeMap = new HashMap<>(); - Coding x0 = new Coding(B, H, S, del); - Coding x1 = codeMap.get(x0); - if (x1 == null) codeMap.put(x0, x1 = x0); - return x1; - } - - public static Coding of(int B, int H) { - return of(B, H, 0, 0); - } - - public static Coding of(int B, int H, int S) { - return of(B, H, S, 0); - } - - public boolean canRepresentValue(int x) { - if (isSubrange()) - return canRepresentUnsigned(x); - else - return canRepresentSigned(x); - } - /** Can this coding represent a single value, possibly a delta? - * This ignores the D property. That is, for delta codings, - * this tests whether a delta value of 'x' can be coded. - * For signed delta codings which produce unsigned end values, - * use canRepresentUnsigned. - */ - public boolean canRepresentSigned(int x) { - return (x >= min && x <= max); - } - /** Can this coding, apart from its S property, - * represent a single value? (Negative values - * can only be represented via 32-bit overflow, - * so this returns true for negative values - * if isFullRange is true.) - */ - public boolean canRepresentUnsigned(int x) { - return (x >= umin && x <= umax); - } - - // object-oriented code/decode - public int readFrom(byte[] in, int[] inpos) { - return readInt(in, inpos, B, H, S); - } - public void writeTo(byte[] out, int[] outpos, int x) { - writeInt(out, outpos, x, B, H, S); - } - - // Stream versions - public int readFrom(InputStream in) throws IOException { - return readIntFrom(in, B, H, S); - } - public void writeTo(OutputStream out, int x) throws IOException { - byte[] buf = new byte[B]; - int[] pos = new int[1]; - writeInt(buf, pos, x, B, H, S); - out.write(buf, 0, pos[0]); - } - - // Stream/array versions - public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException { - // %%% use byte[] buffer - for (int i = start; i < end; i++) - a[i] = readFrom(in); - - for (int dstep = 0; dstep < del; dstep++) { - long state = 0; - for (int i = start; i < end; i++) { - state += a[i]; - // Reduce array values to the required range. - if (isSubrange()) { - state = reduceToUnsignedRange(state); - } - a[i] = (int) state; - } - } - } - public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException { - if (end <= start) return; - for (int dstep = 0; dstep < del; dstep++) { - int[] deltas; - if (!isSubrange()) - deltas = makeDeltas(a, start, end, 0, 0); - else - deltas = makeDeltas(a, start, end, min, max); - a = deltas; - start = 0; - end = deltas.length; - } - // The following code is a buffered version of this loop: - // for (int i = start; i < end; i++) - // writeTo(out, a[i]); - byte[] buf = new byte[1<<8]; - final int bufmax = buf.length-B; - int[] pos = { 0 }; - for (int i = start; i < end; ) { - while (pos[0] <= bufmax) { - writeTo(buf, pos, a[i++]); - if (i >= end) break; - } - out.write(buf, 0, pos[0]); - pos[0] = 0; - } - } - - /** Tell if the range of this coding (number of distinct - * representable values) can be expressed in 32 bits. - */ - boolean isSubrange() { - return max < Integer.MAX_VALUE - && ((long)max - (long)min + 1) <= Integer.MAX_VALUE; - } - - /** Tell if this coding can represent all 32-bit values. - * Note: Some codings, such as unsigned ones, can be neither - * subranges nor full-range codings. - */ - boolean isFullRange() { - return max == Integer.MAX_VALUE && min == Integer.MIN_VALUE; - } - - /** Return the number of values this coding (a subrange) can represent. */ - int getRange() { - assert(isSubrange()); - return (max - min) + 1; // range includes both min & max - } - - Coding setB(int B) { return Coding.of(B, H, S, del); } - Coding setH(int H) { return Coding.of(B, H, S, del); } - Coding setS(int S) { return Coding.of(B, H, S, del); } - Coding setL(int L) { return setH(256-L); } - Coding setD(int del) { return Coding.of(B, H, S, del); } - Coding getDeltaCoding() { return setD(del+1); } - - /** Return a coding suitable for representing summed, modulo-reduced values. */ - Coding getValueCoding() { - if (isDelta()) - return Coding.of(B, H, 0, del-1); - else - return this; - } - - /** Reduce the given value to be within this coding's unsigned range, - * by adding or subtracting a multiple of (max-min+1). - */ - int reduceToUnsignedRange(long value) { - if (value == (int)value && canRepresentUnsigned((int)value)) - // already in unsigned range - return (int)value; - int range = getRange(); - assert(range > 0); - value %= range; - if (value < 0) value += range; - assert(canRepresentUnsigned((int)value)); - return (int)value; - } - - int reduceToSignedRange(int value) { - if (canRepresentSigned(value)) - // already in signed range - return value; - return reduceToSignedRange(value, min, max); - } - static int reduceToSignedRange(int value, int min, int max) { - int range = (max-min+1); - assert(range > 0); - int value0 = value; - value -= min; - if (value < 0 && value0 >= 0) { - // 32-bit overflow, but the next '%=' op needs to be unsigned - value -= range; - assert(value >= 0); - } - value %= range; - if (value < 0) value += range; - value += min; - assert(min <= value && value <= max); - return value; - } - - /** Does this coding support at least one negative value? - Includes codings that can do so via 32-bit wraparound. - */ - boolean isSigned() { - return min < 0; - } - /** Does this coding code arrays by making successive differences? */ - boolean isDelta() { - return del != 0; - } - - public int B() { return B; } - public int H() { return H; } - public int L() { return L; } - public int S() { return S; } - public int del() { return del; } - public int min() { return min; } - public int max() { return max; } - public int umin() { return umin; } - public int umax() { return umax; } - public int byteMin(int b) { return byteMin[b-1]; } - public int byteMax(int b) { return byteMax[b-1]; } - - public int compareTo(Coding that) { - int dkey = this.del - that.del; - if (dkey == 0) - dkey = this.B - that.B; - if (dkey == 0) - dkey = this.H - that.H; - if (dkey == 0) - dkey = this.S - that.S; - return dkey; - } - - /** Heuristic measure of the difference between two codings. */ - public int distanceFrom(Coding that) { - int diffdel = this.del - that.del; - if (diffdel < 0) diffdel = -diffdel; - int diffS = this.S - that.S; - if (diffS < 0) diffS = -diffS; - int diffB = this.B - that.B; - if (diffB < 0) diffB = -diffB; - int diffHL; - if (this.H == that.H) { - diffHL = 0; - } else { - // Distance in log space of H (<=128) and L (<128). - int thisHL = this.getHL(); - int thatHL = that.getHL(); - // Double the accuracy of the log: - thisHL *= thisHL; - thatHL *= thatHL; - if (thisHL > thatHL) - diffHL = ceil_lg2(1+(thisHL-1)/thatHL); - else - diffHL = ceil_lg2(1+(thatHL-1)/thisHL); - } - int norm = 5*(diffdel + diffS + diffB) + diffHL; - assert(norm != 0 || this.compareTo(that) == 0); - return norm; - } - private int getHL() { - // Follow H in log space by the multiplicative inverse of L. - if (H <= 128) return H; - if (L >= 1) return 128*128/L; - return 128*256; - } - - /** ceiling(log[2](x)): {1->0, 2->1, 3->2, 4->2, ...} */ - static int ceil_lg2(int x) { - assert(x-1 >= 0); // x in range (int.MIN_VALUE -> 32) - x -= 1; - int lg = 0; - while (x != 0) { - lg++; - x >>= 1; - } - return lg; - } - - private static final byte[] byteBitWidths = new byte[0x100]; - static { - for (int b = 0; b < byteBitWidths.length; b++) { - byteBitWidths[b] = (byte) ceil_lg2(b + 1); - } - for (int i = 10; i >= 0; i = (i << 1) - (i >> 3)) { - assert(bitWidth(i) == ceil_lg2(i + 1)); - } - } - - /** Number of significant bits in i, not counting sign bits. - * For positive i, it is ceil_lg2(i + 1). - */ - static int bitWidth(int i) { - if (i < 0) i = ~i; // change sign - int w = 0; - int lo = i; - if (lo < byteBitWidths.length) - return byteBitWidths[lo]; - int hi; - hi = (lo >>> 16); - if (hi != 0) { - lo = hi; - w += 16; - } - hi = (lo >>> 8); - if (hi != 0) { - lo = hi; - w += 8; - } - w += byteBitWidths[lo]; - //assert(w == ceil_lg2(i + 1)); - return w; - } - - /** Create an array of successive differences. - * If min==max, accept any and all 32-bit overflow. - * Otherwise, avoid 32-bit overflow, and reduce all differences - * to a value in the given range, by adding or subtracting - * multiples of the range cardinality (max-min+1). - * Also, the values are assumed to be in the range [0..(max-min)]. - */ - static int[] makeDeltas(int[] values, int start, int end, - int min, int max) { - assert(max >= min); - int count = end-start; - int[] deltas = new int[count]; - int state = 0; - if (min == max) { - for (int i = 0; i < count; i++) { - int value = values[start+i]; - deltas[i] = value - state; - state = value; - } - } else { - for (int i = 0; i < count; i++) { - int value = values[start+i]; - assert(value >= 0 && value+min <= max); - int delta = value - state; - assert(delta == (long)value - (long)state); // no overflow - state = value; - // Reduce delta values to the required range. - delta = reduceToSignedRange(delta, min, max); - deltas[i] = delta; - } - } - return deltas; - } - - boolean canRepresent(int minValue, int maxValue) { - assert(minValue <= maxValue); - if (del > 0) { - if (isSubrange()) { - // We will force the values to reduce to the right subrange. - return canRepresentUnsigned(maxValue) - && canRepresentUnsigned(minValue); - } else { - // Huge range; delta values must assume full 32-bit range. - return isFullRange(); - } - } - else - // final values must be representable - return canRepresentSigned(maxValue) - && canRepresentSigned(minValue); - } - - boolean canRepresent(int[] values, int start, int end) { - int len = end-start; - if (len == 0) return true; - if (isFullRange()) return true; - // Calculate max, min: - int lmax = values[start]; - int lmin = lmax; - for (int i = 1; i < len; i++) { - int value = values[start+i]; - if (lmax < value) lmax = value; - if (lmin > value) lmin = value; - } - return canRepresent(lmin, lmax); - } - - public double getBitLength(int value) { // implements BitMetric - return (double) getLength(value) * 8; - } - - /** How many bytes are in the coding of this value? - * Returns Integer.MAX_VALUE if the value has no coding. - * The coding must not be a delta coding, since there is no - * definite size for a single value apart from its context. - */ - public int getLength(int value) { - if (isDelta() && isSubrange()) { - if (!canRepresentUnsigned(value)) - return Integer.MAX_VALUE; - value = reduceToSignedRange(value); - } - if (value >= 0) { - for (int n = 0; n < B; n++) { - if (value <= byteMax[n]) return n+1; - } - } else { - for (int n = 0; n < B; n++) { - if (value >= byteMin[n]) return n+1; - } - } - return Integer.MAX_VALUE; - } - - public int getLength(int[] values, int start, int end) { - int len = end-start; - if (B == 1) return len; - if (L == 0) return len * B; - if (isDelta()) { - int[] deltas; - if (!isSubrange()) - deltas = makeDeltas(values, start, end, 0, 0); - else - deltas = makeDeltas(values, start, end, min, max); - //return Coding.of(B, H, S).getLength(deltas, 0, len); - values = deltas; - start = 0; - } - int sum = len; // at least 1 byte per - // add extra bytes for extra-long values - for (int n = 1; n <= B; n++) { - // what is the coding interval [min..max] for n bytes? - int lmax = byteMax[n-1]; - int lmin = byteMin[n-1]; - int longer = 0; // count of guys longer than n bytes - for (int i = 0; i < len; i++) { - int value = values[start+i]; - if (value >= 0) { - if (value > lmax) longer++; - } else { - if (value < lmin) longer++; - } - } - if (longer == 0) break; // no more passes needed - if (n == B) return Integer.MAX_VALUE; // cannot represent! - sum += longer; - } - return sum; - } - - public byte[] getMetaCoding(Coding dflt) { - if (dflt == this) return new byte[]{ (byte) _meta_default }; - int canonicalIndex = BandStructure.indexOf(this); - if (canonicalIndex > 0) - return new byte[]{ (byte) canonicalIndex }; - return new byte[]{ - (byte)_meta_arb, - (byte)(del + 2*S + 8*(B-1)), - (byte)(H-1) - }; - } - public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) { - int op = bytes[pos++] & 0xFF; - if (_meta_canon_min <= op && op <= _meta_canon_max) { - Coding c = BandStructure.codingForIndex(op); - assert(c != null); - res[0] = c; - return pos; - } - if (op == _meta_arb) { - int dsb = bytes[pos++] & 0xFF; - int H_1 = bytes[pos++] & 0xFF; - int del = dsb % 2; - int S = (dsb / 2) % 4; - int B = (dsb / 8)+1; - int H = H_1+1; - if (!((1 <= B && B <= B_MAX) && - (0 <= S && S <= S_MAX) && - (1 <= H && H <= H_MAX) && - (0 <= del && del <= 1)) - || (B == 1 && H != 256) - || (B == 5 && H == 256)) { - throw new RuntimeException("Bad arb. coding: ("+B+","+H+","+S+","+del); - } - res[0] = Coding.of(B, H, S, del); - return pos; - } - return pos-1; // backup - } - - - public String keyString() { - return "("+B+","+H+","+S+","+del+")"; - } - - public String toString() { - String str = "Coding"+keyString(); - // If -ea, print out more informative strings! - //assert((str = stringForDebug()) != null); - return str; - } - - static boolean verboseStringForDebug = false; - String stringForDebug() { - String minS = (min == Integer.MIN_VALUE ? "min" : ""+min); - String maxS = (max == Integer.MAX_VALUE ? "max" : ""+max); - String str = keyString()+" L="+L+" r=["+minS+","+maxS+"]"; - if (isSubrange()) - str += " subrange"; - else if (!isFullRange()) - str += " MIDRANGE"; - if (verboseStringForDebug) { - str += " {"; - int prev_range = 0; - for (int n = 1; n <= B; n++) { - int range_n = saturate32((long)byteMax[n-1] - byteMin[n-1] + 1); - assert(range_n == saturate32(codeRangeLong(B, H, n))); - range_n -= prev_range; - prev_range = range_n; - String rngS = (range_n == Integer.MAX_VALUE ? "max" : ""+range_n); - str += " #"+n+"="+rngS; - } - str += " }"; - } - return str; - } -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/CodingChooser.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1489 +0,0 @@ -/* - * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import java.util.Set; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; -import static com.sun.java.util.jar.pack.Constants.*; -/** - * Heuristic chooser of basic encodings. - * Runs "zip" to measure the apparent information content after coding. - * @author John Rose - */ -class CodingChooser { - int verbose; - int effort; - boolean optUseHistogram = true; - boolean optUsePopulationCoding = true; - boolean optUseAdaptiveCoding = true; - boolean disablePopCoding; - boolean disableRunCoding; - boolean topLevel = true; - - // Derived from effort; >1 (<1) means try more (less) experiments - // when looking to beat a best score. - double fuzz; - - Coding[] allCodingChoices; - Choice[] choices; - ByteArrayOutputStream context; - CodingChooser popHelper; - CodingChooser runHelper; - - Random stress; // If not null, stress mode oracle. - - // Element in sorted set of coding choices: - static - class Choice { - final Coding coding; - final int index; // index in choices - final int[] distance; // cache of distance - Choice(Coding coding, int index, int[] distance) { - this.coding = coding; - this.index = index; - this.distance = distance; - } - // These variables are reset and reused: - int searchOrder; // order in which it is checked - int minDistance; // min distance from already-checked choices - int zipSize; // size of encoding in sample, zipped output - int byteSize; // size of encoding in sample (debug only) - int histSize; // size of encoding, according to histogram - - void reset() { - searchOrder = Integer.MAX_VALUE; - minDistance = Integer.MAX_VALUE; - zipSize = byteSize = histSize = -1; - } - - boolean isExtra() { - return index < 0; - } - - public String toString() { - return stringForDebug(); - } - - private String stringForDebug() { - String s = ""; - if (searchOrder < Integer.MAX_VALUE) - s += " so: "+searchOrder; - if (minDistance < Integer.MAX_VALUE) - s += " md: "+minDistance; - if (zipSize > 0) - s += " zs: "+zipSize; - if (byteSize > 0) - s += " bs: "+byteSize; - if (histSize > 0) - s += " hs: "+histSize; - return "Choice["+index+"] "+s+" "+coding; - } - } - - CodingChooser(int effort, Coding[] allCodingChoices) { - PropMap p200 = Utils.currentPropMap(); - if (p200 != null) { - this.verbose - = Math.max(p200.getInteger(Utils.DEBUG_VERBOSE), - p200.getInteger(Utils.COM_PREFIX+"verbose.coding")); - this.optUseHistogram - = !p200.getBoolean(Utils.COM_PREFIX+"no.histogram"); - this.optUsePopulationCoding - = !p200.getBoolean(Utils.COM_PREFIX+"no.population.coding"); - this.optUseAdaptiveCoding - = !p200.getBoolean(Utils.COM_PREFIX+"no.adaptive.coding"); - int lstress - = p200.getInteger(Utils.COM_PREFIX+"stress.coding"); - if (lstress != 0) - this.stress = new Random(lstress); - } - - this.effort = effort; - // The following line "makes sense" but is too much - // work for a simple heuristic. - //if (effort > 5) zipDef.setLevel(effort); - - this.allCodingChoices = allCodingChoices; - - // If effort = 9, look carefully at any solution - // whose initial metrics are within 1% of the best - // so far. If effort = 1, look carefully only at - // solutions whose initial metrics promise a 1% win. - this.fuzz = 1 + (0.0025 * (effort-MID_EFFORT)); - - int nc = 0; - for (int i = 0; i < allCodingChoices.length; i++) { - if (allCodingChoices[i] == null) continue; - nc++; - } - choices = new Choice[nc]; - nc = 0; - for (int i = 0; i < allCodingChoices.length; i++) { - if (allCodingChoices[i] == null) continue; - int[] distance = new int[choices.length]; - choices[nc++] = new Choice(allCodingChoices[i], i, distance); - } - for (int i = 0; i < choices.length; i++) { - Coding ci = choices[i].coding; - assert(ci.distanceFrom(ci) == 0); - for (int j = 0; j < i; j++) { - Coding cj = choices[j].coding; - int dij = ci.distanceFrom(cj); - assert(dij > 0); - assert(dij == cj.distanceFrom(ci)); - choices[i].distance[j] = dij; - choices[j].distance[i] = dij; - } - } - } - - Choice makeExtraChoice(Coding coding) { - int[] distance = new int[choices.length]; - for (int i = 0; i < distance.length; i++) { - Coding ci = choices[i].coding; - int dij = coding.distanceFrom(ci); - assert(dij > 0); - assert(dij == ci.distanceFrom(coding)); - distance[i] = dij; - } - Choice c = new Choice(coding, -1, distance); - c.reset(); - return c; - } - - ByteArrayOutputStream getContext() { - if (context == null) - context = new ByteArrayOutputStream(1 << 16); - return context; - } - - // These variables are reset and reused: - private int[] values; - private int start, end; // slice of values - private int[] deltas; - private int min, max; - private Histogram vHist; - private Histogram dHist; - private int searchOrder; - private Choice regularChoice; - private Choice bestChoice; - private CodingMethod bestMethod; - private int bestByteSize; - private int bestZipSize; - private int targetSize; // fuzzed target byte size - - private void reset(int[] values, int start, int end) { - this.values = values; - this.start = start; - this.end = end; - this.deltas = null; - this.min = Integer.MAX_VALUE; - this.max = Integer.MIN_VALUE; - this.vHist = null; - this.dHist = null; - this.searchOrder = 0; - this.regularChoice = null; - this.bestChoice = null; - this.bestMethod = null; - this.bestZipSize = Integer.MAX_VALUE; - this.bestByteSize = Integer.MAX_VALUE; - this.targetSize = Integer.MAX_VALUE; - } - - public static final int MIN_EFFORT = 1; - public static final int MID_EFFORT = 5; - public static final int MAX_EFFORT = 9; - - public static final int POP_EFFORT = MID_EFFORT-1; - public static final int RUN_EFFORT = MID_EFFORT-2; - - public static final int BYTE_SIZE = 0; - public static final int ZIP_SIZE = 1; - - CodingMethod choose(int[] values, int start, int end, Coding regular, int[] sizes) { - // Save the value array - reset(values, start, end); - - if (effort <= MIN_EFFORT || start >= end) { - if (sizes != null) { - int[] computed = computeSizePrivate(regular); - sizes[BYTE_SIZE] = computed[BYTE_SIZE]; - sizes[ZIP_SIZE] = computed[ZIP_SIZE]; - } - return regular; - } - - if (optUseHistogram) { - getValueHistogram(); - getDeltaHistogram(); - } - - for (int i = start; i < end; i++) { - int val = values[i]; - if (min > val) min = val; - if (max < val) max = val; - } - - // Find all the preset choices that might be worth looking at: - int numChoices = markUsableChoices(regular); - - if (stress != null) { - // Make a random choice. - int rand = stress.nextInt(numChoices*2 + 4); - CodingMethod coding = null; - for (int i = 0; i < choices.length; i++) { - Choice c = choices[i]; - if (c.searchOrder >= 0 && rand-- == 0) { - coding = c.coding; - break; - } - } - if (coding == null) { - if ((rand & 7) != 0) { - coding = regular; - } else { - // Pick a totally random coding 6% of the time. - coding = stressCoding(min, max); - } - } - if (!disablePopCoding - && optUsePopulationCoding - && effort >= POP_EFFORT) { - coding = stressPopCoding(coding); - } - if (!disableRunCoding - && optUseAdaptiveCoding - && effort >= RUN_EFFORT) { - coding = stressAdaptiveCoding(coding); - } - return coding; - } - - double searchScale = 1.0; - for (int x = effort; x < MAX_EFFORT; x++) { - searchScale /= 1.414; // every 2 effort points doubles work - } - int searchOrderLimit = (int)Math.ceil( numChoices * searchScale ); - - // Start by evaluating the "regular" choice. - bestChoice = regularChoice; - evaluate(regularChoice); - int maxd = updateDistances(regularChoice); - - // save these first-cut numbers for later - int zipSize1 = bestZipSize; - int byteSize1 = bestByteSize; - - if (regularChoice.coding == regular && topLevel) { - // Give credit for being the default; no band header is needed. - // Rather than increasing every other size value by the band - // header amount, we decrement this one metric, to give it an edge. - // Decreasing zipSize by a byte length is conservatively correct, - // especially considering that the escape byte is not likely to - // zip well with other bytes in the band. - int X = BandStructure.encodeEscapeValue(_meta_canon_max, regular); - if (regular.canRepresentSigned(X)) { - int Xlen = regular.getLength(X); // band coding header - //regularChoice.histSize -= Xlen; // keep exact byteSize - //regularChoice.byteSize -= Xlen; // keep exact byteSize - regularChoice.zipSize -= Xlen; - bestByteSize = regularChoice.byteSize; - bestZipSize = regularChoice.zipSize; - } - } - - int dscale = 1; - // Continually select a new choice to evaluate. - while (searchOrder < searchOrderLimit) { - Choice nextChoice; - if (dscale > maxd) dscale = 1; // cycle dscale values! - int dhi = maxd / dscale; - int dlo = maxd / (dscale *= 2) + 1; - nextChoice = findChoiceNear(bestChoice, dhi, dlo); - if (nextChoice == null) continue; - assert(nextChoice.coding.canRepresent(min, max)); - evaluate(nextChoice); - int nextMaxd = updateDistances(nextChoice); - if (nextChoice == bestChoice) { - maxd = nextMaxd; - if (verbose > 5) Utils.log.info("maxd = "+maxd); - } - } - - // Record best "plain coding" choice. - Coding plainBest = bestChoice.coding; - assert(plainBest == bestMethod); - - if (verbose > 2) { - Utils.log.info("chooser: plain result="+bestChoice+" after "+bestChoice.searchOrder+" rounds, "+(regularChoice.zipSize-bestZipSize)+" fewer bytes than regular "+regular); - } - bestChoice = null; - - if (!disablePopCoding - && optUsePopulationCoding - && effort >= POP_EFFORT - && bestMethod instanceof Coding) { - tryPopulationCoding(plainBest); - } - - if (!disableRunCoding - && optUseAdaptiveCoding - && effort >= RUN_EFFORT - && bestMethod instanceof Coding) { - tryAdaptiveCoding(plainBest); - } - - // Pass back the requested information: - if (sizes != null) { - sizes[BYTE_SIZE] = bestByteSize; - sizes[ZIP_SIZE] = bestZipSize; - } - if (verbose > 1) { - Utils.log.info("chooser: result="+bestMethod+" "+ - (zipSize1-bestZipSize)+ - " fewer bytes than regular "+regular+ - "; win="+pct(zipSize1-bestZipSize, zipSize1)); - } - CodingMethod lbestMethod = this.bestMethod; - reset(null, 0, 0); // for GC - return lbestMethod; - } - CodingMethod choose(int[] values, int start, int end, Coding regular) { - return choose(values, start, end, regular, null); - } - CodingMethod choose(int[] values, Coding regular, int[] sizes) { - return choose(values, 0, values.length, regular, sizes); - } - CodingMethod choose(int[] values, Coding regular) { - return choose(values, 0, values.length, regular, null); - } - - private int markUsableChoices(Coding regular) { - int numChoices = 0; - for (int i = 0; i < choices.length; i++) { - Choice c = choices[i]; - c.reset(); - if (!c.coding.canRepresent(min, max)) { - // Mark as already visited: - c.searchOrder = -1; - if (verbose > 1 && c.coding == regular) { - Utils.log.info("regular coding cannot represent ["+min+".."+max+"]: "+regular); - } - continue; - } - if (c.coding == regular) - regularChoice = c; - numChoices++; - } - if (regularChoice == null && regular.canRepresent(min, max)) { - regularChoice = makeExtraChoice(regular); - if (verbose > 1) { - Utils.log.info("*** regular choice is extra: "+regularChoice.coding); - } - } - if (regularChoice == null) { - for (int i = 0; i < choices.length; i++) { - Choice c = choices[i]; - if (c.searchOrder != -1) { - regularChoice = c; // arbitrary pick - break; - } - } - if (verbose > 1) { - Utils.log.info("*** regular choice does not apply "+regular); - Utils.log.info(" using instead "+regularChoice.coding); - } - } - if (verbose > 2) { - Utils.log.info("chooser: #choices="+numChoices+" ["+min+".."+max+"]"); - if (verbose > 4) { - for (int i = 0; i < choices.length; i++) { - Choice c = choices[i]; - if (c.searchOrder >= 0) - Utils.log.info(" "+c); - } - } - } - return numChoices; - } - - // Find an arbitrary choice at least dlo away from a previously - // evaluated choices, and at most dhi. Try also to regulate its - // min distance to all previously evaluated choices, in this range. - private Choice findChoiceNear(Choice near, int dhi, int dlo) { - if (verbose > 5) - Utils.log.info("findChoice "+dhi+".."+dlo+" near: "+near); - int[] distance = near.distance; - Choice found = null; - for (int i = 0; i < choices.length; i++) { - Choice c = choices[i]; - if (c.searchOrder < searchOrder) - continue; // already searched - // Distance from "near" guy must be in bounds: - if (distance[i] >= dlo && distance[i] <= dhi) { - // Try also to keep min-distance from other guys in bounds: - if (c.minDistance >= dlo && c.minDistance <= dhi) { - if (verbose > 5) - Utils.log.info("findChoice => good "+c); - return c; - } - found = c; - } - } - if (verbose > 5) - Utils.log.info("findChoice => found "+found); - return found; - } - - private void evaluate(Choice c) { - assert(c.searchOrder == Integer.MAX_VALUE); - c.searchOrder = searchOrder++; - boolean mustComputeSize; - if (c == bestChoice || c.isExtra()) { - mustComputeSize = true; - } else if (optUseHistogram) { - Histogram hist = getHistogram(c.coding.isDelta()); - c.histSize = (int)Math.ceil(hist.getBitLength(c.coding) / 8); - c.byteSize = c.histSize; - mustComputeSize = (c.byteSize <= targetSize); - } else { - mustComputeSize = true; - } - if (mustComputeSize) { - int[] sizes = computeSizePrivate(c.coding); - c.byteSize = sizes[BYTE_SIZE]; - c.zipSize = sizes[ZIP_SIZE]; - if (noteSizes(c.coding, c.byteSize, c.zipSize)) - bestChoice = c; - } - if (c.histSize >= 0) { - assert(c.byteSize == c.histSize); // models should agree - } - if (verbose > 4) { - Utils.log.info("evaluated "+c); - } - } - - private boolean noteSizes(CodingMethod c, int byteSize, int zipSize) { - assert(zipSize > 0 && byteSize > 0); - boolean better = (zipSize < bestZipSize); - if (verbose > 3) - Utils.log.info("computed size "+c+" "+byteSize+"/zs="+zipSize+ - ((better && bestMethod != null)? - (" better by "+ - pct(bestZipSize - zipSize, zipSize)): "")); - if (better) { - bestMethod = c; - bestZipSize = zipSize; - bestByteSize = byteSize; - targetSize = (int)(byteSize * fuzz); - return true; - } else { - return false; - } - } - - - private int updateDistances(Choice c) { - // update all minDistance values in still unevaluated choices - int[] distance = c.distance; - int maxd = 0; // how far is c from everybody else? - for (int i = 0; i < choices.length; i++) { - Choice c2 = choices[i]; - if (c2.searchOrder < searchOrder) - continue; - int d = distance[i]; - if (verbose > 5) - Utils.log.info("evaluate dist "+d+" to "+c2); - int mind = c2.minDistance; - if (mind > d) - c2.minDistance = mind = d; - if (maxd < d) - maxd = d; - } - // Now maxd has the distance of the farthest outlier - // from all evaluated choices. - if (verbose > 5) - Utils.log.info("evaluate maxd => "+maxd); - return maxd; - } - - // Compute the coded size of a sequence of values. - // The first int is the size in uncompressed bytes. - // The second is an estimate of the compressed size of these bytes. - public void computeSize(CodingMethod c, int[] values, int start, int end, int[] sizes) { - if (end <= start) { - sizes[BYTE_SIZE] = sizes[ZIP_SIZE] = 0; - return; - } - try { - resetData(); - c.writeArrayTo(byteSizer, values, start, end); - sizes[BYTE_SIZE] = getByteSize(); - sizes[ZIP_SIZE] = getZipSize(); - } catch (IOException ee) { - throw new RuntimeException(ee); // cannot happen - } - } - public void computeSize(CodingMethod c, int[] values, int[] sizes) { - computeSize(c, values, 0, values.length, sizes); - } - public int[] computeSize(CodingMethod c, int[] values, int start, int end) { - int[] sizes = { 0, 0 }; - computeSize(c, values, start, end, sizes); - return sizes; - } - public int[] computeSize(CodingMethod c, int[] values) { - return computeSize(c, values, 0, values.length); - } - // This version uses the implicit local arguments - private int[] computeSizePrivate(CodingMethod c) { - int[] sizes = { 0, 0 }; - computeSize(c, values, start, end, sizes); - return sizes; - } - public int computeByteSize(CodingMethod cm, int[] values, int start, int end) { - int len = end-start; - if (len < 0) { - return 0; - } - if (cm instanceof Coding) { - Coding c = (Coding) cm; - int size = c.getLength(values, start, end); - int size2; - assert(size == (size2=countBytesToSizer(cm, values, start, end))) - : (cm+" : "+size+" != "+size2); - return size; - } - return countBytesToSizer(cm, values, start, end); - } - private int countBytesToSizer(CodingMethod cm, int[] values, int start, int end) { - try { - byteOnlySizer.reset(); - cm.writeArrayTo(byteOnlySizer, values, start, end); - return byteOnlySizer.getSize(); - } catch (IOException ee) { - throw new RuntimeException(ee); // cannot happen - } - } - - int[] getDeltas(int min, int max) { - if ((min|max) != 0) - return Coding.makeDeltas(values, start, end, min, max); - if (deltas == null) { - deltas = Coding.makeDeltas(values, start, end, 0, 0); - } - return deltas; - } - Histogram getValueHistogram() { - if (vHist == null) { - vHist = new Histogram(values, start, end); - if (verbose > 3) { - vHist.print("vHist", System.out); - } else if (verbose > 1) { - vHist.print("vHist", null, System.out); - } - } - return vHist; - } - Histogram getDeltaHistogram() { - if (dHist == null) { - dHist = new Histogram(getDeltas(0, 0)); - if (verbose > 3) { - dHist.print("dHist", System.out); - } else if (verbose > 1) { - dHist.print("dHist", null, System.out); - } - } - return dHist; - } - Histogram getHistogram(boolean isDelta) { - return isDelta ? getDeltaHistogram(): getValueHistogram(); - } - - private void tryPopulationCoding(Coding plainCoding) { - // assert(plainCoding.canRepresent(min, max)); - Histogram hist = getValueHistogram(); - // Start with "reasonable" default codings. - final int approxL = 64; - Coding favoredCoding = plainCoding.getValueCoding(); - Coding tokenCoding = BandStructure.UNSIGNED5.setL(approxL); - Coding unfavoredCoding = plainCoding.getValueCoding(); - // There's going to be a band header. Estimate conservatively large. - final int BAND_HEADER = 4; - // Keep a running model of the predicted sizes of the F/T/U sequences. - int currentFSize; - int currentTSize; - int currentUSize; - // Start by assuming a degenerate favored-value length of 0, - // which looks like a bunch of zero tokens followed by the - // original sequence. - // The {F} list ends with a repeated F value; find worst case: - currentFSize = - BAND_HEADER + Math.max(favoredCoding.getLength(min), - favoredCoding.getLength(max)); - // The {T} list starts out a bunch of zeros, each of length 1. - final int ZERO_LEN = tokenCoding.getLength(0); - currentTSize = ZERO_LEN * (end-start); - // The {U} list starts out a copy of the plainCoding: - currentUSize = (int) Math.ceil(hist.getBitLength(unfavoredCoding) / 8); - - int bestPopSize = (currentFSize + currentTSize + currentUSize); - int bestPopFVC = 0; - - // Record all the values, in decreasing order of favor. - int[] allFavoredValues = new int[1+hist.getTotalLength()]; - //int[] allPopSizes = new int[1+hist.getTotalLength()]; - - // What sizes are "interesting"? - int targetLowFVC = -1; - int targetHighFVC = -1; - - // For each length, adjust the currentXSize model, and look for a win. - int[][] matrix = hist.getMatrix(); - int mrow = -1; - int mcol = 1; - int mrowFreq = 0; - for (int fvcount = 1; fvcount <= hist.getTotalLength(); fvcount++) { - // The {F} list gets an additional member. - // Take it from the end of the current matrix row. - // (It's the end, so that we get larger favored values first.) - if (mcol == 1) { - mrow += 1; - mrowFreq = matrix[mrow][0]; - mcol = matrix[mrow].length; - } - int thisValue = matrix[mrow][--mcol]; - allFavoredValues[fvcount] = thisValue; - int thisVLen = favoredCoding.getLength(thisValue); - currentFSize += thisVLen; - // The token list replaces occurrences of zero with a new token: - int thisVCount = mrowFreq; - int thisToken = fvcount; - currentTSize += (tokenCoding.getLength(thisToken) - - ZERO_LEN) * thisVCount; - // The unfavored list loses occurrences of the newly favored value. - // (This is the whole point of the exercise!) - currentUSize -= thisVLen * thisVCount; - int currentSize = (currentFSize + currentTSize + currentUSize); - //allPopSizes[fvcount] = currentSize; - if (bestPopSize > currentSize) { - if (currentSize <= targetSize) { - targetHighFVC = fvcount; - if (targetLowFVC < 0) - targetLowFVC = fvcount; - if (verbose > 4) - Utils.log.info("better pop-size at fvc="+fvcount+ - " by "+pct(bestPopSize-currentSize, - bestPopSize)); - } - bestPopSize = currentSize; - bestPopFVC = fvcount; - } - } - if (targetLowFVC < 0) { - if (verbose > 1) { - // Complete loss. - if (verbose > 1) - Utils.log.info("no good pop-size; best was "+ - bestPopSize+" at "+bestPopFVC+ - " worse by "+ - pct(bestPopSize-bestByteSize, - bestByteSize)); - } - return; - } - if (verbose > 1) - Utils.log.info("initial best pop-size at fvc="+bestPopFVC+ - " in ["+targetLowFVC+".."+targetHighFVC+"]"+ - " by "+pct(bestByteSize-bestPopSize, - bestByteSize)); - int oldZipSize = bestZipSize; - // Now close onto a specific coding, testing more rigorously - // with the zipSize metric. - // Questions to decide: - // 1. How many favored values? - // 2. What token coding (TC)? - // 3. Sort favored values by value within length brackets? - // 4. What favored coding? - // 5. What unfavored coding? - // Steps 1/2/3 are interdependent, and may be iterated. - // Steps 4 and 5 may be decided independently afterward. - int[] LValuesCoded = PopulationCoding.LValuesCoded; - List<Coding> bestFits = new ArrayList<>(); - List<Coding> fullFits = new ArrayList<>(); - List<Coding> longFits = new ArrayList<>(); - final int PACK_TO_MAX_S = 1; - if (bestPopFVC <= 255) { - bestFits.add(BandStructure.BYTE1); - } else { - int bestB = Coding.B_MAX; - boolean doFullAlso = (effort > POP_EFFORT); - if (doFullAlso) - fullFits.add(BandStructure.BYTE1.setS(PACK_TO_MAX_S)); - for (int i = LValuesCoded.length-1; i >= 1; i--) { - int L = LValuesCoded[i]; - Coding c0 = PopulationCoding.fitTokenCoding(targetLowFVC, L); - Coding c1 = PopulationCoding.fitTokenCoding(bestPopFVC, L); - Coding c3 = PopulationCoding.fitTokenCoding(targetHighFVC, L); - if (c1 != null) { - if (!bestFits.contains(c1)) - bestFits.add(c1); - if (bestB > c1.B()) - bestB = c1.B(); - } - if (doFullAlso) { - if (c3 == null) c3 = c1; - for (int B = c0.B(); B <= c3.B(); B++) { - if (B == c1.B()) continue; - if (B == 1) continue; - Coding c2 = c3.setB(B).setS(PACK_TO_MAX_S); - if (!fullFits.contains(c2)) - fullFits.add(c2); - } - } - } - // interleave all B greater than bestB with best and full fits - for (Iterator<Coding> i = bestFits.iterator(); i.hasNext(); ) { - Coding c = i.next(); - if (c.B() > bestB) { - i.remove(); - longFits.add(0, c); - } - } - } - List<Coding> allFits = new ArrayList<>(); - for (Iterator<Coding> i = bestFits.iterator(), - j = fullFits.iterator(), - k = longFits.iterator(); - i.hasNext() || j.hasNext() || k.hasNext(); ) { - if (i.hasNext()) allFits.add(i.next()); - if (j.hasNext()) allFits.add(j.next()); - if (k.hasNext()) allFits.add(k.next()); - } - bestFits.clear(); - fullFits.clear(); - longFits.clear(); - int maxFits = allFits.size(); - if (effort == POP_EFFORT) - maxFits = 2; - else if (maxFits > 4) { - maxFits -= 4; - maxFits = (maxFits * (effort-POP_EFFORT) - ) / (MAX_EFFORT-POP_EFFORT); - maxFits += 4; - } - if (allFits.size() > maxFits) { - if (verbose > 4) - Utils.log.info("allFits before clip: "+allFits); - allFits.subList(maxFits, allFits.size()).clear(); - } - if (verbose > 3) - Utils.log.info("allFits: "+allFits); - for (Coding tc : allFits) { - boolean packToMax = false; - if (tc.S() == PACK_TO_MAX_S) { - // Kludge: setS(PACK_TO_MAX_S) means packToMax here. - packToMax = true; - tc = tc.setS(0); - } - int fVlen; - if (!packToMax) { - fVlen = bestPopFVC; - assert(tc.umax() >= fVlen); - assert(tc.B() == 1 || tc.setB(tc.B()-1).umax() < fVlen); - } else { - fVlen = Math.min(tc.umax(), targetHighFVC); - if (fVlen < targetLowFVC) - continue; - if (fVlen == bestPopFVC) - continue; // redundant test - } - PopulationCoding pop = new PopulationCoding(); - pop.setHistogram(hist); - pop.setL(tc.L()); - pop.setFavoredValues(allFavoredValues, fVlen); - assert(pop.tokenCoding == tc); // predict correctly - pop.resortFavoredValues(); - int[] tcsizes = - computePopSizePrivate(pop, - favoredCoding, unfavoredCoding); - noteSizes(pop, tcsizes[BYTE_SIZE], BAND_HEADER+tcsizes[ZIP_SIZE]); - } - if (verbose > 3) { - Utils.log.info("measured best pop, size="+bestByteSize+ - "/zs="+bestZipSize+ - " better by "+ - pct(oldZipSize-bestZipSize, oldZipSize)); - if (bestZipSize < oldZipSize) { - Utils.log.info(">>> POP WINS BY "+ - (oldZipSize - bestZipSize)); - } - } - } - - private - int[] computePopSizePrivate(PopulationCoding pop, - Coding favoredCoding, - Coding unfavoredCoding) { - if (popHelper == null) { - popHelper = new CodingChooser(effort, allCodingChoices); - if (stress != null) - popHelper.addStressSeed(stress.nextInt()); - popHelper.topLevel = false; - popHelper.verbose -= 1; - popHelper.disablePopCoding = true; - popHelper.disableRunCoding = this.disableRunCoding; - if (effort < MID_EFFORT) - // No nested run codings. - popHelper.disableRunCoding = true; - } - int fVlen = pop.fVlen; - if (verbose > 2) { - Utils.log.info("computePopSizePrivate fvlen="+fVlen+ - " tc="+pop.tokenCoding); - Utils.log.info("{ //BEGIN"); - } - - // Find good coding choices for the token and unfavored sequences. - int[] favoredValues = pop.fValues; - int[][] vals = pop.encodeValues(values, start, end); - int[] tokens = vals[0]; - int[] unfavoredValues = vals[1]; - if (verbose > 2) - Utils.log.info("-- refine on fv["+fVlen+"] fc="+favoredCoding); - pop.setFavoredCoding(popHelper.choose(favoredValues, 1, 1+fVlen, favoredCoding)); - if (pop.tokenCoding instanceof Coding && - (stress == null || stress.nextBoolean())) { - if (verbose > 2) - Utils.log.info("-- refine on tv["+tokens.length+"] tc="+pop.tokenCoding); - CodingMethod tc = popHelper.choose(tokens, (Coding) pop.tokenCoding); - if (tc != pop.tokenCoding) { - if (verbose > 2) - Utils.log.info(">>> refined tc="+tc); - pop.setTokenCoding(tc); - } - } - if (unfavoredValues.length == 0) - pop.setUnfavoredCoding(null); - else { - if (verbose > 2) - Utils.log.info("-- refine on uv["+unfavoredValues.length+"] uc="+pop.unfavoredCoding); - pop.setUnfavoredCoding(popHelper.choose(unfavoredValues, unfavoredCoding)); - } - if (verbose > 3) { - Utils.log.info("finish computePopSizePrivate fvlen="+fVlen+ - " fc="+pop.favoredCoding+ - " tc="+pop.tokenCoding+ - " uc="+pop.unfavoredCoding); - //pop.hist.print("pop-hist", null, System.out); - StringBuilder sb = new StringBuilder(); - sb.append("fv = {"); - for (int i = 1; i <= fVlen; i++) { - if ((i % 10) == 0) - sb.append('\n'); - sb.append(" ").append(favoredValues[i]); - } - sb.append('\n'); - sb.append("}"); - Utils.log.info(sb.toString()); - } - if (verbose > 2) { - Utils.log.info("} //END"); - } - if (stress != null) { - return null; // do not bother with size computation - } - int[] sizes; - try { - resetData(); - // Write the array of favored values. - pop.writeSequencesTo(byteSizer, tokens, unfavoredValues); - sizes = new int[] { getByteSize(), getZipSize() }; - } catch (IOException ee) { - throw new RuntimeException(ee); // cannot happen - } - int[] checkSizes = null; - assert((checkSizes = computeSizePrivate(pop)) != null); - assert(checkSizes[BYTE_SIZE] == sizes[BYTE_SIZE]) - : (checkSizes[BYTE_SIZE]+" != "+sizes[BYTE_SIZE]); - return sizes; - } - - private void tryAdaptiveCoding(Coding plainCoding) { - int oldZipSize = bestZipSize; - // Scan the value sequence, determining whether an interesting - // run occupies too much space. ("Too much" means, say 5% more - // than the average integer size of the band as a whole.) - // Try to find a better coding for those segments. - int lstart = this.start; - int lend = this.end; - int[] lvalues = this.values; - int len = lend-lstart; - if (plainCoding.isDelta()) { - lvalues = getDeltas(0,0); //%%% not quite right! - lstart = 0; - lend = lvalues.length; - } - int[] sizes = new int[len+1]; - int fillp = 0; - int totalSize = 0; - for (int i = lstart; i < lend; i++) { - int val = lvalues[i]; - sizes[fillp++] = totalSize; - int size = plainCoding.getLength(val); - assert(size < Integer.MAX_VALUE); - //System.out.println("len "+val+" = "+size); - totalSize += size; - } - sizes[fillp++] = totalSize; - assert(fillp == sizes.length); - double avgSize = (double)totalSize / len; - double sizeFuzz; - double sizeFuzz2; - double sizeFuzz3; - if (effort >= MID_EFFORT) { - if (effort > MID_EFFORT+1) - sizeFuzz = 1.001; - else - sizeFuzz = 1.003; - } else { - if (effort > RUN_EFFORT) - sizeFuzz = 1.01; - else - sizeFuzz = 1.03; - } - // for now: - sizeFuzz *= sizeFuzz; // double the thresh - sizeFuzz2 = (sizeFuzz*sizeFuzz); - sizeFuzz3 = (sizeFuzz*sizeFuzz*sizeFuzz); - // Find some mesh scales we like. - double[] dmeshes = new double[1 + (effort-RUN_EFFORT)]; - double logLen = Math.log(len); - for (int i = 0; i < dmeshes.length; i++) { - dmeshes[i] = Math.exp(logLen*(i+1)/(dmeshes.length+1)); - } - int[] meshes = new int[dmeshes.length]; - int mfillp = 0; - for (int i = 0; i < dmeshes.length; i++) { - int m = (int)Math.round(dmeshes[i]); - m = AdaptiveCoding.getNextK(m-1); - if (m <= 0 || m >= len) continue; - if (mfillp > 0 && m == meshes[mfillp-1]) continue; - meshes[mfillp++] = m; - } - meshes = BandStructure.realloc(meshes, mfillp); - // There's going to be a band header. Estimate conservatively large. - final int BAND_HEADER = 4; // op, KB, A, B - // Threshold values for a "too big" mesh. - int[] threshes = new int[meshes.length]; - double[] fuzzes = new double[meshes.length]; - for (int i = 0; i < meshes.length; i++) { - int mesh = meshes[i]; - double lfuzz; - if (mesh < 10) - lfuzz = sizeFuzz3; - else if (mesh < 100) - lfuzz = sizeFuzz2; - else - lfuzz = sizeFuzz; - fuzzes[i] = lfuzz; - threshes[i] = BAND_HEADER + (int)Math.ceil(mesh * avgSize * lfuzz); - } - if (verbose > 1) { - System.out.print("tryAdaptiveCoding ["+len+"]"+ - " avgS="+avgSize+" fuzz="+sizeFuzz+ - " meshes: {"); - for (int i = 0; i < meshes.length; i++) { - System.out.print(" " + meshes[i] + "(" + threshes[i] + ")"); - } - Utils.log.info(" }"); - } - if (runHelper == null) { - runHelper = new CodingChooser(effort, allCodingChoices); - if (stress != null) - runHelper.addStressSeed(stress.nextInt()); - runHelper.topLevel = false; - runHelper.verbose -= 1; - runHelper.disableRunCoding = true; - runHelper.disablePopCoding = this.disablePopCoding; - if (effort < MID_EFFORT) - // No nested pop codings. - runHelper.disablePopCoding = true; - } - for (int i = 0; i < len; i++) { - i = AdaptiveCoding.getNextK(i-1); - if (i > len) i = len; - for (int j = meshes.length-1; j >= 0; j--) { - int mesh = meshes[j]; - int thresh = threshes[j]; - if (i+mesh > len) continue; - int size = sizes[i+mesh] - sizes[i]; - if (size >= thresh) { - // Found a size bulge. - int bend = i+mesh; - int bsize = size; - double bigSize = avgSize * fuzzes[j]; - while (bend < len && (bend-i) <= len/2) { - int bend0 = bend; - int bsize0 = bsize; - bend += mesh; - bend = i+AdaptiveCoding.getNextK(bend-i-1); - if (bend < 0 || bend > len) - bend = len; - bsize = sizes[bend]-sizes[i]; - if (bsize < BAND_HEADER + (bend-i) * bigSize) { - bsize = bsize0; - bend = bend0; - break; - } - } - int nexti = bend; - if (verbose > 2) { - Utils.log.info("bulge at "+i+"["+(bend-i)+"] of "+ - pct(bsize - avgSize*(bend-i), - avgSize*(bend-i))); - Utils.log.info("{ //BEGIN"); - } - CodingMethod begcm, midcm, endcm; - midcm = runHelper.choose(this.values, - this.start+i, - this.start+bend, - plainCoding); - if (midcm == plainCoding) { - // No use working further. - begcm = plainCoding; - endcm = plainCoding; - } else { - begcm = runHelper.choose(this.values, - this.start, - this.start+i, - plainCoding); - endcm = runHelper.choose(this.values, - this.start+bend, - this.start+len, - plainCoding); - } - if (verbose > 2) - Utils.log.info("} //END"); - if (begcm == midcm && i > 0 && - AdaptiveCoding.isCodableLength(bend)) { - i = 0; - } - if (midcm == endcm && bend < len) { - bend = len; - } - if (begcm != plainCoding || - midcm != plainCoding || - endcm != plainCoding) { - CodingMethod chain; - int hlen = 0; - if (bend == len) { - chain = midcm; - } else { - chain = new AdaptiveCoding(bend-i, midcm, endcm); - hlen += BAND_HEADER; - } - if (i > 0) { - chain = new AdaptiveCoding(i, begcm, chain); - hlen += BAND_HEADER; - } - int[] chainSize = computeSizePrivate(chain); - noteSizes(chain, - chainSize[BYTE_SIZE], - chainSize[ZIP_SIZE]+hlen); - } - i = nexti; - break; - } - } - } - if (verbose > 3) { - if (bestZipSize < oldZipSize) { - Utils.log.info(">>> RUN WINS BY "+ - (oldZipSize - bestZipSize)); - } - } - } - - private static - String pct(double num, double den) { - return (Math.round((num / den)*10000)/100.0)+"%"; - } - - static - class Sizer extends OutputStream { - final OutputStream out; // if non-null, copy output here also - Sizer(OutputStream out) { - this.out = out; - } - Sizer() { - this(null); - } - private int count; - public void write(int b) throws IOException { - count++; - if (out != null) out.write(b); - } - public void write(byte b[], int off, int len) throws IOException { - count += len; - if (out != null) out.write(b, off, len); - } - public void reset() { - count = 0; - } - public int getSize() { return count; } - - public String toString() { - String str = super.toString(); - // If -ea, print out more informative strings! - assert((str = stringForDebug()) != null); - return str; - } - String stringForDebug() { - return "<Sizer "+getSize()+">"; - } - } - - private Sizer zipSizer = new Sizer(); - private Deflater zipDef = new Deflater(); - private DeflaterOutputStream zipOut = new DeflaterOutputStream(zipSizer, zipDef); - private Sizer byteSizer = new Sizer(zipOut); - private Sizer byteOnlySizer = new Sizer(); - - private void resetData() { - flushData(); - zipDef.reset(); - if (context != null) { - // Prepend given salt to the test output. - try { - context.writeTo(byteSizer); - } catch (IOException ee) { - throw new RuntimeException(ee); // cannot happen - } - } - zipSizer.reset(); - byteSizer.reset(); - } - private void flushData() { - try { - zipOut.finish(); - } catch (IOException ee) { - throw new RuntimeException(ee); // cannot happen - } - } - private int getByteSize() { - return byteSizer.getSize(); - } - private int getZipSize() { - flushData(); - return zipSizer.getSize(); - } - - - /// Stress-test helpers. - - void addStressSeed(int x) { - if (stress == null) return; - stress.setSeed(x + ((long)stress.nextInt() << 32)); - } - - // Pick a random pop-coding. - private CodingMethod stressPopCoding(CodingMethod coding) { - assert(stress != null); // this method is only for testing - // Don't turn it into a pop coding if it's already something special. - if (!(coding instanceof Coding)) return coding; - Coding valueCoding = ((Coding)coding).getValueCoding(); - Histogram hist = getValueHistogram(); - int fVlen = stressLen(hist.getTotalLength()); - if (fVlen == 0) return coding; - List<Integer> popvals = new ArrayList<>(); - if (stress.nextBoolean()) { - // Build the population from the value list. - Set<Integer> popset = new HashSet<>(); - for (int i = start; i < end; i++) { - if (popset.add(values[i])) popvals.add(values[i]); - } - } else { - int[][] matrix = hist.getMatrix(); - for (int mrow = 0; mrow < matrix.length; mrow++) { - int[] row = matrix[mrow]; - for (int mcol = 1; mcol < row.length; mcol++) { - popvals.add(row[mcol]); - } - } - } - int reorder = stress.nextInt(); - if ((reorder & 7) <= 2) { - // Lose the order. - Collections.shuffle(popvals, stress); - } else { - // Keep the order, mostly. - if (((reorder >>>= 3) & 7) <= 2) Collections.sort(popvals); - if (((reorder >>>= 3) & 7) <= 2) Collections.reverse(popvals); - if (((reorder >>>= 3) & 7) <= 2) Collections.rotate(popvals, stressLen(popvals.size())); - } - if (popvals.size() > fVlen) { - // Cut the list down. - if (((reorder >>>= 3) & 7) <= 2) { - // Cut at end. - popvals.subList(fVlen, popvals.size()).clear(); - } else { - // Cut at start. - popvals.subList(0, popvals.size()-fVlen).clear(); - } - } - fVlen = popvals.size(); - int[] fvals = new int[1+fVlen]; - for (int i = 0; i < fVlen; i++) { - fvals[1+i] = (popvals.get(i)).intValue(); - } - PopulationCoding pop = new PopulationCoding(); - pop.setFavoredValues(fvals, fVlen); - int[] lvals = PopulationCoding.LValuesCoded; - for (int i = 0; i < lvals.length / 2; i++) { - int popl = lvals[stress.nextInt(lvals.length)]; - if (popl < 0) continue; - if (PopulationCoding.fitTokenCoding(fVlen, popl) != null) { - pop.setL(popl); - break; - } - } - if (pop.tokenCoding == null) { - int lmin = fvals[1], lmax = lmin; - for (int i = 2; i <= fVlen; i++) { - int val = fvals[i]; - if (lmin > val) lmin = val; - if (lmax < val) lmax = val; - } - pop.tokenCoding = stressCoding(lmin, lmax); - } - - computePopSizePrivate(pop, valueCoding, valueCoding); - return pop; - } - - // Pick a random adaptive coding. - private CodingMethod stressAdaptiveCoding(CodingMethod coding) { - assert(stress != null); // this method is only for testing - // Don't turn it into a run coding if it's already something special. - if (!(coding instanceof Coding)) return coding; - Coding plainCoding = (Coding)coding; - int len = end-start; - if (len < 2) return coding; - // Decide how many spans we'll create. - int spanlen = stressLen(len-1)+1; - if (spanlen == len) return coding; - try { - assert(!disableRunCoding); - disableRunCoding = true; // temporary, while I decide spans - int[] allValues = values.clone(); - CodingMethod result = null; - int scan = this.end; - int lstart = this.start; - for (int split; scan > lstart; scan = split) { - int thisspan; - int rand = (scan - lstart < 100)? -1: stress.nextInt(); - if ((rand & 7) != 0) { - thisspan = (spanlen==1? spanlen: stressLen(spanlen-1)+1); - } else { - // Every so often generate a value based on KX/KB format. - int KX = (rand >>>= 3) & AdaptiveCoding.KX_MAX; - int KB = (rand >>>= 3) & AdaptiveCoding.KB_MAX; - for (;;) { - thisspan = AdaptiveCoding.decodeK(KX, KB); - if (thisspan <= scan - lstart) break; - // Try smaller and smaller codings: - if (KB != AdaptiveCoding.KB_DEFAULT) - KB = AdaptiveCoding.KB_DEFAULT; - else - KX -= 1; - } - //System.out.println("KX="+KX+" KB="+KB+" K="+thisspan); - assert(AdaptiveCoding.isCodableLength(thisspan)); - } - if (thisspan > scan - lstart) thisspan = scan - lstart; - while (!AdaptiveCoding.isCodableLength(thisspan)) { - --thisspan; - } - split = scan - thisspan; - assert(split < scan); - assert(split >= lstart); - // Choose a coding for the span [split..scan). - CodingMethod sc = choose(allValues, split, scan, plainCoding); - if (result == null) { - result = sc; // the caboose - } else { - result = new AdaptiveCoding(scan-split, sc, result); - } - } - return result; - } finally { - disableRunCoding = false; // return to normal value - } - } - - // Return a random value in [0..len], gently biased toward extremes. - private Coding stressCoding(int min, int max) { - assert(stress != null); // this method is only for testing - for (int i = 0; i < 100; i++) { - Coding c = Coding.of(stress.nextInt(Coding.B_MAX)+1, - stress.nextInt(Coding.H_MAX)+1, - stress.nextInt(Coding.S_MAX+1)); - if (c.B() == 1) c = c.setH(256); - if (c.H() == 256 && c.B() >= 5) c = c.setB(4); - if (stress.nextBoolean()) { - Coding dc = c.setD(1); - if (dc.canRepresent(min, max)) return dc; - } - if (c.canRepresent(min, max)) return c; - } - return BandStructure.UNSIGNED5; - } - - // Return a random value in [0..len], gently biased toward extremes. - private int stressLen(int len) { - assert(stress != null); // this method is only for testing - assert(len >= 0); - int rand = stress.nextInt(100); - if (rand < 20) - return Math.min(len/5, rand); - else if (rand < 40) - return len; - else - return stress.nextInt(len); - } - - // For debug only. -/* - public static - int[] readValuesFrom(InputStream instr) { - return readValuesFrom(new InputStreamReader(instr)); - } - public static - int[] readValuesFrom(Reader inrdr) { - inrdr = new BufferedReader(inrdr); - final StreamTokenizer in = new StreamTokenizer(inrdr); - final int TT_NOTHING = -99; - in.commentChar('#'); - return readValuesFrom(new Iterator() { - int token = TT_NOTHING; - private int getToken() { - if (token == TT_NOTHING) { - try { - token = in.nextToken(); - assert(token != TT_NOTHING); - } catch (IOException ee) { - throw new RuntimeException(ee); - } - } - return token; - } - public boolean hasNext() { - return getToken() != StreamTokenizer.TT_EOF; - } - public Object next() { - int ntok = getToken(); - token = TT_NOTHING; - switch (ntok) { - case StreamTokenizer.TT_EOF: - throw new NoSuchElementException(); - case StreamTokenizer.TT_NUMBER: - return Integer.valueOf((int) in.nval); - default: - assert(false); - return null; - } - } - public void remove() { - throw new UnsupportedOperationException(); - } - }); - } - public static - int[] readValuesFrom(Iterator iter) { - return readValuesFrom(iter, 0); - } - public static - int[] readValuesFrom(Iterator iter, int initSize) { - int[] na = new int[Math.max(10, initSize)]; - int np = 0; - while (iter.hasNext()) { - Integer val = (Integer) iter.next(); - if (np == na.length) { - na = BandStructure.realloc(na); - } - na[np++] = val.intValue(); - } - if (np != na.length) { - na = BandStructure.realloc(na, np); - } - return na; - } - - public static - void main(String[] av) throws IOException { - int effort = MID_EFFORT; - int ap = 0; - if (ap < av.length && av[ap].equals("-e")) { - ap++; - effort = Integer.parseInt(av[ap++]); - } - int verbose = 1; - if (ap < av.length && av[ap].equals("-v")) { - ap++; - verbose = Integer.parseInt(av[ap++]); - } - Coding[] bcs = BandStructure.getBasicCodings(); - CodingChooser cc = new CodingChooser(effort, bcs); - if (ap < av.length && av[ap].equals("-p")) { - ap++; - cc.optUsePopulationCoding = false; - } - if (ap < av.length && av[ap].equals("-a")) { - ap++; - cc.optUseAdaptiveCoding = false; - } - cc.verbose = verbose; - int[] values = readValuesFrom(System.in); - int[] sizes = {0,0}; - CodingMethod cm = cc.choose(values, BandStructure.UNSIGNED5, sizes); - System.out.println("size: "+sizes[BYTE_SIZE]+"/zs="+sizes[ZIP_SIZE]); - System.out.println(cm); - } -//*/ - -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/CodingMethod.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Interface for encoding and decoding int arrays using bytewise codes. - * @author John Rose - */ -interface CodingMethod { - // Read and write an array of ints from/to a stream. - public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException; - public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException; - - // how to express me in a band header? - public byte[] getMetaCoding(Coding dflt); -}
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/ConstantPool.java Mon Dec 09 15:28:46 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1658 +0,0 @@ -/* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.java.util.jar.pack; - -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; -import static com.sun.java.util.jar.pack.Constants.*; - -/** - * Representation of constant pool entries and indexes. - * @author John Rose - */ -abstract -class ConstantPool { - private ConstantPool() {} // do not instantiate - - static int verbose() { - return Utils.currentPropMap().getInteger(Utils.DEBUG_VERBOSE); - } - - /** Factory for Utf8 string constants. - * Used for well-known strings like "SourceFile", "<init>", etc. - * Also used to back up more complex constant pool entries, like Class. - */ - public static synchronized Utf8Entry getUtf8Entry(String value) { - Map<String, Utf8Entry> utf8Entries = Utils.getTLGlobals().getUtf8Entries(); - Utf8Entry e = utf8Entries.get(value); - if (e == null) { - e = new Utf8Entry(value); - utf8Entries.put(e.stringValue(), e); - } - return e; - } - /** Factory for Class constants. */ - public static ClassEntry getClassEntry(String name) { - Map<String, ClassEntry> classEntries = Utils.getTLGlobals().getClassEntries(); - ClassEntry e = classEntries.get(name); - if (e == null) { - e = new ClassEntry(getUtf8Entry(name)); - assert(name.equals(e.stringValue())); - classEntries.put(e.stringValue(), e); - } - return e; - } - /** Factory for literal constants (String, Integer, etc.). */ - public static LiteralEntry getLiteralEntry(Comparable<?> value) { - Map<Object, LiteralEntry> literalEntries = Utils.getTLGlobals().getLiteralEntries(); - LiteralEntry e = literalEntries.get(value); - if (e == null) { - if (value instanceof String) - e = new StringEntry(getUtf8Entry((String)value)); - else - e = new NumberEntry((Number)value); - literalEntries.put(value, e); - } - return e; - } - /** Factory for literal constants (String, Integer, etc.). */ - public static StringEntry getStringEntry(String value) { - return (StringEntry) getLiteralEntry(value); - } - - /** Factory for signature (type) constants. */ - public static SignatureEntry getSignatureEntry(String type) { - Map<String, SignatureEntry> signatureEntries = Utils.getTLGlobals().getSignatureEntries(); - SignatureEntry e = signatureEntries.get(type); - if (e == null) { - e = new SignatureEntry(type); - assert(e.stringValue().equals(type)); - signatureEntries.put(type, e); - } - return e; - } - // Convenience overloading. - public static SignatureEntry getSignatureEntry(Utf8Entry formRef, ClassEntry[] classRefs) { - return getSignatureEntry(SignatureEntry.stringValueOf(formRef, classRefs)); - } - - /** Factory for descriptor (name-and-type) constants. */ - public static DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) { - Map<String, DescriptorEntry> descriptorEntries = Utils.getTLGlobals().getDescriptorEntries(); - String key = DescriptorEntry.stringValueOf(nameRef, typeRef); - DescriptorEntry e = descriptorEntries.get(key); - if (e == null) { - e = new DescriptorEntry(nameRef, typeRef); - assert(e.stringValue().equals(key)) - : (e.stringValue()+" != "+(key)); - descriptorEntries.put(key, e); - } - return e; - } - // Convenience overloading. - public static DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, Utf8Entry typeRef) { - return getDescriptorEntry(nameRef, getSignatureEntry(typeRef.stringValue())); - } - - /** Factory for member reference constants. */ - public static MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) { - Map<String, MemberEntry> memberEntries = Utils.getTLGlobals().getMemberEntries(); - String key = MemberEntry.stringValueOf(tag, classRef, descRef); - MemberEntry e = memberEntries.get(key); - if (e == null) { - e = new MemberEntry(tag, classRef, descRef); - assert(e.stringValue().equals(key)) - : (e.stringValue()+" != "+(key)); - memberEntries.put(key, e); - } - return e; - } - - /** Factory for MethodHandle constants. */ - public static MethodHandleEntry getMethodHandleEntry(byte refKind, MemberEntry memRef) { - Map<String, MethodHandleEntry> methodHandleEntries = Utils.getTLGlobals().getMethodHandleEntries(); - String key = MethodHandleEntry.stringValueOf(refKind, memRef); - MethodHandleEntry e = methodHandleEntries.get(key); - if (e == null) { - e = new MethodHandleEntry(refKind, memRef); - assert(e.stringValue().equals(key)); - methodHandleEntries.put(key, e); - } - return e; - } - - /** Factory for MethodType constants. */ - public static MethodTypeEntry getMethodTypeEntry(SignatureEntry sigRef) { - Map<String, MethodTypeEntry> methodTypeEntries = Utils.getTLGlobals().getMethodTypeEntries(); - String key = sigRef.stringValue(); - MethodTypeEntry e = methodTypeEntries.get(key); - if (e == null) { - e = new MethodTypeEntry(sigRef); - assert(e.stringValue().equals(key)); - methodTypeEntries.put(key, e); - } - return e; - } - public static MethodTypeEntry getMethodTypeEntry(Utf8Entry typeRef) { - return getMethodTypeEntry(getSignatureEntry(typeRef.stringValue())); - } - - /** Factory for InvokeDynamic constants. */ - public static InvokeDynamicEntry getInvokeDynamicEntry(BootstrapMethodEntry bssRef, DescriptorEntry descRef) { - Map<String, InvokeDynamicEntry> invokeDynamicEntries = Utils.getTLGlobals().getInvokeDynamicEntries(); - String key = InvokeDynamicEntry.stringValueOf(bssRef, descRef); - InvokeDynamicEntry e = invokeDynamicEntries.get(key); - if (e == null) { - e = new InvokeDynamicEntry(bssRef, descRef); - assert(e.stringValue().equals(key)); - invokeDynamicEntries.put(key, e); - } - return e; - } - - /** Factory for BootstrapMethod pseudo-constants. */ - public static BootstrapMethodEntry getBootstrapMethodEntry(MethodHandleEntry bsmRef, Entry[] argRefs) { - Map<String, BootstrapMethodEntry> bootstrapMethodEntries = Utils.getTLGlobals().getBootstrapMethodEntries(); - String key = BootstrapMethodEntry.stringValueOf(bsmRef, argRefs); - BootstrapMethodEntry e = bootstrapMethodEntries.get(key); - if (e == null) { - e = new BootstrapMethodEntry(bsmRef, argRefs); - assert(e.stringValue().equals(key)); - bootstrapMethodEntries.put(key, e); - } - return e; - } - - - /** Entries in the constant pool. */ - public abstract static - class Entry implements Comparable<Object> { - protected final byte tag; // a CONSTANT_foo code - protected int valueHash; // cached hashCode - - protected Entry(byte tag) { - this.tag = tag; - } - - public final byte getTag() { - return tag; - } - - public final boolean tagEquals(int tag) { - return getTag() == tag; - } - - public Entry getRef(int i) { - return null; - } - - public boolean eq(Entry that) { // same reference - assert(that != null); - return this == that || this.equals(that); - } - - // Equality of Entries is value-based. - public abstract boolean equals(Object o); - public final int hashCode() { - if (valueHash == 0) { - valueHash = computeValueHash(); - if (valueHash == 0) valueHash = 1; - } - return valueHash; - } - protected abstract int computeValueHash(); - - public abstract int compareTo(Object o); - - protected int superCompareTo(Object o) { - Entry that = (Entry) o; - - if (this.tag != that.tag) { - return TAG_ORDER[this.tag] - TAG_ORDER[that.tag]; - } - - return 0; // subclasses must refine this - } - - public final boolean isDoubleWord() { - return tag == CONSTANT_Double || tag == CONSTANT_Long; - } - - public final boolean tagMatches(int matchTag) { - if (tag == matchTag) - return true; - byte[] allowedTags; - switch (matchTag) { - case CONSTANT_All: - return true; - case CONSTANT_Signature: - return tag == CONSTANT_Utf8; // format check also? - case CONSTANT_LoadableValue: - allowedTags = LOADABLE_VALUE_TAGS; - break; - case CONSTANT_AnyMember: - allowedTags = ANY_MEMBER_TAGS; - break; - case CONSTANT_FieldSpecific: - allowedTags = FIELD_SPECIFIC_TAGS; - break; - default: - return false; - } - for (byte b : allowedTags) { - if (b == tag) - return true; - } - return false; - } - - public String toString() { - String valuePrint = stringValue(); - if (verbose() > 4) { - if (valueHash != 0) - valuePrint += " hash="+valueHash; - valuePrint += " id="+System.identityHashCode(this); - } - return tagName(tag)+"="+valuePrint; - } - public abstract String stringValue(); - } - - public static - class Utf8Entry extends Entry { - final String value; - - Utf8Entry(String value) { - super(CONSTANT_Utf8); - this.value = value.intern(); - hashCode(); // force computation of valueHash - } - protected int computeValueHash() { - return value.hashCode(); - } - public boolean equals(Object o) { - // Use reference equality of interned strings: - return (o != null && o.getClass() == Utf8Entry.class - && ((Utf8Entry) o).value.equals(value)); - } - public int compareTo(Object o) { - int x = superCompareTo(o); - if (x == 0) { - x = value.compareTo(((Utf8Entry)o).value); - } - return x; - } - public String stringValue() { - return value; - } - } - - static boolean isMemberTag(byte tag) { - switch (tag) { - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - return true; - } - return false; - } - - static byte numberTagOf(Number value) { - if (value instanceof Integer) return CONSTANT_Integer; - if (value instanceof Float) return CONSTANT_Float; - if (value instanceof Long) return CONSTANT_Long; - if (value instanceof Double) return CONSTANT_Double; - throw new RuntimeException("bad literal value "+value); - } - - static boolean isRefKind(byte refKind) { - return (REF_getField <= refKind && refKind <= REF_invokeInterface); - } - - public abstract static - class LiteralEntry extends Entry { - protected LiteralEntry(byte tag) { -