OpenJDK / amber / amber
changeset 59290:fa409cbc32ad
Merge
author | psadhukhan |
---|---|
date | Tue, 10 Dec 2019 10:44:09 +0530 |
parents | aa0f481d0024 b2e191f03473 |
children | 6e16ceda3b96 |
files | make/launcher/Launcher-jdk.pack.gmk make/lib/Lib-jdk.pack.gmk 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.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 test/jdk/ProblemList.txt test/jdk/java/util/jar/Pack200/SecurityTest.java test/jdk/tools/jar/DeprecateOptionN.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 | 382 files changed, 1829 insertions(+), 51074 deletions(-) [+] |
line wrap: on
line diff
--- a/make/autoconf/compare.sh.in Mon Dec 09 14:59:33 2019 -0800 +++ b/make/autoconf/compare.sh.in Tue Dec 10 10:44:09 2019 +0530 @@ -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 14:59:33 2019 -0800 +++ b/make/common/Modules.gmk Tue Dec 10 10:44:09 2019 +0530 @@ -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/gendata/Gendata-jdk.compiler.gmk Mon Dec 09 14:59:33 2019 -0800 +++ b/make/gendata/Gendata-jdk.compiler.gmk Tue Dec 10 10:44:09 2019 +0530 @@ -45,6 +45,7 @@ CT_DATA_DESCRIPTION += $(TOPDIR)/make/data/symbols/symbols COMPILECREATESYMBOLS_ADD_EXPORTS := \ + --add-exports java.base/jdk.internal=java.compiler.interim,jdk.compiler.interim \ --add-exports jdk.compiler.interim/com.sun.tools.javac.api=ALL-UNNAMED \ --add-exports jdk.compiler.interim/com.sun.tools.javac.code=ALL-UNNAMED \ --add-exports jdk.compiler.interim/com.sun.tools.javac.util=ALL-UNNAMED \ @@ -58,6 +59,7 @@ INCLUDES := build/tools/symbolgenerator com/sun/tools/classfile, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/create_symbols, \ ADD_JAVAC_FLAGS := $(INTERIM_LANGTOOLS_ARGS) \ + --patch-module java.base=$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim \ $(COMPILECREATESYMBOLS_ADD_EXPORTS), \ ))
--- a/make/launcher/Launcher-jdk.pack.gmk Mon Dec 09 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ b/make/nashorn/element-list Tue Dec 10 10:44:09 2019 +0530 @@ -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 14:59:33 2019 -0800 +++ b/make/scripts/compare.sh Tue Dec 10 10:44:09 2019 +0530 @@ -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 14:59:33 2019 -0800 +++ b/make/scripts/compare_exceptions.sh.incl Tue Dec 10 10:44:09 2019 +0530 @@ -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/hotspot/cpu/x86/assembler_x86.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/assembler_x86.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -7232,7 +7232,7 @@ } void Assembler::evpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, int mask, int vector_len) { - assert(VM_Version::supports_vpclmulqdq(), "Requires vector carryless multiplication support"); + assert(VM_Version::supports_avx512_vpclmulqdq(), "Requires vector carryless multiplication support"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_is_evex_instruction(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -374,22 +374,41 @@ __ cmpptr(rbx, 0); // rbx contains the incoming method for c2i adapters. __ jcc(Assembler::equal, bad_call); +#ifdef _LP64 + Register tmp1 = rscratch1; + Register tmp2 = rscratch2; +#else + Register tmp1 = rax; + Register tmp2 = rcx; + __ push(tmp1); + __ push(tmp2); +#endif // _LP64 + // Pointer chase to the method holder to find out if the method is concurrently unloading. Label method_live; - __ load_method_holder_cld(rscratch1, rbx); + __ load_method_holder_cld(tmp1, rbx); - // Is it a strong CLD? - __ movl(rscratch2, Address(rscratch1, ClassLoaderData::keep_alive_offset())); - __ cmpptr(rscratch2, 0); + // Is it a strong CLD? + __ cmpl(Address(tmp1, ClassLoaderData::keep_alive_offset()), 0); __ jcc(Assembler::greater, method_live); - // Is it a weak but alive CLD? - __ movptr(rscratch1, Address(rscratch1, ClassLoaderData::holder_offset())); - __ resolve_weak_handle(rscratch1, rscratch2); - __ cmpptr(rscratch1, 0); + // Is it a weak but alive CLD? + __ movptr(tmp1, Address(tmp1, ClassLoaderData::holder_offset())); + __ resolve_weak_handle(tmp1, tmp2); + __ cmpptr(tmp1, 0); __ jcc(Assembler::notEqual, method_live); +#ifndef _LP64 + __ pop(tmp2); + __ pop(tmp1); +#endif + __ bind(bad_call); __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); __ bind(method_live); + +#ifndef _LP64 + __ pop(tmp2); + __ pop(tmp1); +#endif }
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -395,52 +395,6 @@ __ block_comment("load_reference_barrier_native { "); } -#ifdef _LP64 -void ShenandoahBarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { - // Use default version - BarrierSetAssembler::c2i_entry_barrier(masm); -} -#else -void ShenandoahBarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { - BarrierSetNMethod* bs = BarrierSet::barrier_set()->barrier_set_nmethod(); - if (bs == NULL) { - return; - } - - Label bad_call; - __ cmpptr(rbx, 0); // rbx contains the incoming method for c2i adapters. - __ jcc(Assembler::equal, bad_call); - - Register tmp1 = rax; - Register tmp2 = rcx; - - __ push(tmp1); - __ push(tmp2); - - // Pointer chase to the method holder to find out if the method is concurrently unloading. - Label method_live; - __ load_method_holder_cld(tmp1, rbx); - - // Is it a strong CLD? - __ cmpl(Address(tmp1, ClassLoaderData::keep_alive_offset()), 0); - __ jcc(Assembler::greater, method_live); - - // Is it a weak but alive CLD? - __ movptr(tmp1, Address(tmp1, ClassLoaderData::holder_offset())); - __ resolve_weak_handle(tmp1, tmp2); - __ cmpptr(tmp1, 0); - __ jcc(Assembler::notEqual, method_live); - __ pop(tmp2); - __ pop(tmp1); - - __ bind(bad_call); - __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); - __ bind(method_live); - __ pop(tmp2); - __ pop(tmp1); -} -#endif - void ShenandoahBarrierSetAssembler::storeval_barrier(MacroAssembler* masm, Register dst, Register tmp) { if (ShenandoahStoreValEnqueueBarrier) { storeval_barrier_impl(masm, dst, tmp);
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -86,8 +86,6 @@ Address dst, Register val, Register tmp1, Register tmp2); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); - virtual void c2i_entry_barrier(MacroAssembler* masm); - virtual void barrier_stubs_init(); };
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -8945,34 +8945,6 @@ shrl(len, 4); jcc(Assembler::zero, L_tail_restore); - // Fold total 512 bits of polynomial on each iteration - if (VM_Version::supports_vpclmulqdq()) { - Label Parallel_loop, L_No_Parallel; - - cmpl(len, 8); - jccb(Assembler::less, L_No_Parallel); - - movdqu(xmm0, ExternalAddress(StubRoutines::x86::crc_by128_masks_addr() + 32)); - evmovdquq(xmm1, Address(buf, 0), Assembler::AVX_512bit); - movdl(xmm5, crc); - evpxorq(xmm1, xmm1, xmm5, Assembler::AVX_512bit); - addptr(buf, 64); - subl(len, 7); - evshufi64x2(xmm0, xmm0, xmm0, 0x00, Assembler::AVX_512bit); //propagate the mask from 128 bits to 512 bits - - BIND(Parallel_loop); - fold_128bit_crc32_avx512(xmm1, xmm0, xmm5, buf, 0); - addptr(buf, 64); - subl(len, 4); - jcc(Assembler::greater, Parallel_loop); - - vextracti64x2(xmm2, xmm1, 0x01); - vextracti64x2(xmm3, xmm1, 0x02); - vextracti64x2(xmm4, xmm1, 0x03); - jmp(L_fold_512b); - - BIND(L_No_Parallel); - } // Fold crc into first bytes of vector movdqa(xmm1, Address(buf, 0)); movdl(rax, xmm1);
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -691,7 +691,7 @@ _features &= ~CPU_AVX512BW; _features &= ~CPU_AVX512VL; _features &= ~CPU_AVX512_VPOPCNTDQ; - _features &= ~CPU_VPCLMULQDQ; + _features &= ~CPU_AVX512_VPCLMULQDQ; _features &= ~CPU_VAES; }
--- a/src/hotspot/cpu/x86/vm_version_x86.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -245,7 +245,7 @@ : 1, gfni : 1, vaes : 1, - vpclmulqdq : 1, + avx512_vpclmulqdq : 1, avx512_vnni : 1, avx512_bitalg : 1, : 1, @@ -338,7 +338,7 @@ #define CPU_FMA ((uint64_t)UCONST64(0x800000000)) // FMA instructions #define CPU_VZEROUPPER ((uint64_t)UCONST64(0x1000000000)) // Vzeroupper instruction #define CPU_AVX512_VPOPCNTDQ ((uint64_t)UCONST64(0x2000000000)) // Vector popcount -#define CPU_VPCLMULQDQ ((uint64_t)UCONST64(0x4000000000)) //Vector carryless multiplication +#define CPU_AVX512_VPCLMULQDQ ((uint64_t)UCONST64(0x4000000000)) //Vector carryless multiplication #define CPU_VAES ((uint64_t)UCONST64(0x8000000000)) // Vector AES instructions #define CPU_VNNI ((uint64_t)UCONST64(0x10000000000)) // Vector Neural Network Instructions @@ -561,8 +561,8 @@ result |= CPU_AVX512VL; if (_cpuid_info.sef_cpuid7_ecx.bits.avx512_vpopcntdq != 0) result |= CPU_AVX512_VPOPCNTDQ; - if (_cpuid_info.sef_cpuid7_ecx.bits.vpclmulqdq != 0) - result |= CPU_VPCLMULQDQ; + if (_cpuid_info.sef_cpuid7_ecx.bits.avx512_vpclmulqdq != 0) + result |= CPU_AVX512_VPCLMULQDQ; if (_cpuid_info.sef_cpuid7_ecx.bits.vaes != 0) result |= CPU_VAES; if (_cpuid_info.sef_cpuid7_ecx.bits.avx512_vnni != 0) @@ -855,7 +855,7 @@ static bool supports_fma() { return (_features & CPU_FMA) != 0 && supports_avx(); } static bool supports_vzeroupper() { return (_features & CPU_VZEROUPPER) != 0; } static bool supports_vpopcntdq() { return (_features & CPU_AVX512_VPOPCNTDQ) != 0; } - static bool supports_vpclmulqdq() { return (_features & CPU_VPCLMULQDQ) != 0; } + static bool supports_avx512_vpclmulqdq() { return (_features & CPU_AVX512_VPCLMULQDQ) != 0; } static bool supports_vaes() { return (_features & CPU_VAES) != 0; } static bool supports_vnni() { return (_features & CPU_VNNI) != 0; }
--- a/src/hotspot/share/classfile/javaClasses.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/classfile/javaClasses.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -2709,62 +2709,58 @@ java_lang_StackTraceElement::set_fileName(element(), NULL); java_lang_StackTraceElement::set_lineNumber(element(), -1); } else { - // Fill in source file name and line number. - Symbol* source = Backtrace::get_source_file_name(holder, version); - oop source_file = java_lang_Class::source_file(java_class()); - if (source != NULL) { - // Class was not redefined. We can trust its cache if set, - // else we have to initialize it. - if (source_file == NULL) { - source_file = StringTable::intern(source, CHECK); - java_lang_Class::set_source_file(java_class(), source_file); - } - } else { - // Class was redefined. Dump the cache if it was set. - if (source_file != NULL) { - source_file = NULL; - java_lang_Class::set_source_file(java_class(), source_file); - } - } + Symbol* source; + oop source_file; + int line_number; + decode_file_and_line(java_class, holder, version, method, bci, source, source_file, line_number, CHECK); + java_lang_StackTraceElement::set_fileName(element(), source_file); - - int line_number = Backtrace::get_line_number(method(), bci); java_lang_StackTraceElement::set_lineNumber(element(), line_number); } } +void java_lang_StackTraceElement::decode_file_and_line(Handle java_class, + InstanceKlass* holder, + int version, + const methodHandle& method, + int bci, + Symbol*& source, + oop& source_file, + int& line_number, TRAPS) { + // Fill in source file name and line number. + source = Backtrace::get_source_file_name(holder, version); + source_file = java_lang_Class::source_file(java_class()); + if (source != NULL) { + // Class was not redefined. We can trust its cache if set, + // else we have to initialize it. + if (source_file == NULL) { + source_file = StringTable::intern(source, CHECK); + java_lang_Class::set_source_file(java_class(), source_file); + } + } else { + // Class was redefined. Dump the cache if it was set. + if (source_file != NULL) { + source_file = NULL; + java_lang_Class::set_source_file(java_class(), source_file); + } + } + line_number = Backtrace::get_line_number(method(), bci); +} + #if INCLUDE_JVMCI -void java_lang_StackTraceElement::decode(Handle mirror, methodHandle method, int bci, Symbol*& methodname, Symbol*& filename, int& line_number) { - int method_id = method->orig_method_idnum(); - int cpref = method->name_index(); - decode(mirror, method_id, method->constants()->version(), bci, cpref, methodname, filename, line_number); -} - -void java_lang_StackTraceElement::decode(Handle mirror, int method_id, int version, int bci, int cpref, Symbol*& methodname, Symbol*& filename, int& line_number) { - // Fill in class name - InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); - Method* method = holder->method_with_orig_idnum(method_id, version); - - // The method can be NULL if the requested class version is gone - Symbol* sym = (method != NULL) ? method->name() : holder->constants()->symbol_at(cpref); - - // Fill in method name - methodname = sym; - - if (!version_matches(method, version)) { - // If the method was redefined, accurate line number information isn't available - filename = NULL; - line_number = -1; - } else { - // Fill in source file name and line number. - // Use a specific ik version as a holder since the mirror might - // refer to a version that is now obsolete and no longer accessible - // via the previous versions list. - holder = holder->get_klass_version(version); - assert(holder != NULL, "sanity check"); - filename = holder->source_file_name(); - line_number = Backtrace::get_line_number(method, bci); - } +void java_lang_StackTraceElement::decode(const methodHandle& method, int bci, + Symbol*& filename, int& line_number, TRAPS) { + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + + filename = NULL; + line_number = -1; + + oop source_file; + int version = method->constants()->version(); + InstanceKlass* holder = method->method_holder(); + Handle java_class(THREAD, holder->java_mirror()); + decode_file_and_line(java_class, holder, version, method, bci, filename, source_file, line_number, CHECK); } #endif // INCLUDE_JVMCI
--- a/src/hotspot/share/classfile/javaClasses.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/classfile/javaClasses.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -1401,6 +1401,10 @@ static void set_lineNumber(oop element, int value); static void set_declaringClassObject(oop element, oop value); + static void decode_file_and_line(Handle java_mirror, InstanceKlass* holder, int version, + const methodHandle& method, int bci, + Symbol*& source, oop& source_file, int& line_number, TRAPS); + public: // Create an instance of StackTraceElement static oop create(const methodHandle& method, int bci, TRAPS); @@ -1412,8 +1416,7 @@ static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; #if INCLUDE_JVMCI - static void decode(Handle mirror, int method, int version, int bci, int cpref, Symbol*& methodName, Symbol*& fileName, int& lineNumber); - static void decode(Handle mirror, methodHandle method, int bci, Symbol*& methodName, Symbol*& fileName, int& lineNumber); + static void decode(const methodHandle& method, int bci, Symbol*& fileName, int& lineNumber, TRAPS); #endif // Debugging
--- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -597,12 +597,15 @@ Node* adr = access.addr().node(); Node* obj = access.base(); + bool anonymous = (decorators & C2_UNSAFE_ACCESS) != 0; bool mismatched = (decorators & C2_MISMATCHED) != 0; bool unknown = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool in_heap = (decorators & IN_HEAP) != 0; + bool in_native = (decorators & IN_NATIVE) != 0; bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0; bool is_unordered = (decorators & MO_UNORDERED) != 0; - bool need_cpu_mem_bar = !is_unordered || mismatched || !in_heap; + bool is_mixed = !in_heap && !in_native; + bool need_cpu_mem_bar = !is_unordered || mismatched || is_mixed; Node* top = Compile::current()->top(); Node* offset = adr->is_AddP() ? adr->in(AddPNode::Offset) : top;
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -98,6 +98,7 @@ #include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" #include "utilities/align.hpp" +#include "utilities/bitMap.inline.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/stack.inline.hpp" @@ -2347,6 +2348,10 @@ heap_region_iterate(&blk); } +void G1CollectedHeap::keep_alive(oop obj) { + G1BarrierSet::enqueue(obj); +} + void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { _hrm->iterate(cl); } @@ -2366,6 +2371,10 @@ _collection_set.iterate(cl); } +void G1CollectedHeap::collection_set_par_iterate_all(HeapRegionClosure* cl, HeapRegionClaimer* hr_claimer, uint worker_id) { + _collection_set.par_iterate(cl, hr_claimer, worker_id, workers()->active_workers()); +} + void G1CollectedHeap::collection_set_iterate_increment_from(HeapRegionClosure *cl, HeapRegionClaimer* hr_claimer, uint worker_id) { _collection_set.iterate_incremental_part_from(cl, hr_claimer, worker_id, workers()->active_workers()); } @@ -4079,7 +4088,6 @@ assert(!hr->is_free(), "the region should not be free"); assert(!hr->is_empty(), "the region should not be empty"); assert(_hrm->is_available(hr->hrm_index()), "region should be committed"); - assert(free_list != NULL, "pre-condition"); if (G1VerifyBitmaps) { MemRegion mr(hr->bottom(), hr->end()); @@ -4094,7 +4102,9 @@ } hr->hr_clear(skip_remset, true /* clear_space */, locked /* locked */); _policy->remset_tracker()->update_at_free(hr); - free_list->add_ordered(hr); + if (free_list != NULL) { + free_list->add_ordered(hr); + } } void G1CollectedHeap::free_humongous_region(HeapRegion* hr, @@ -4128,281 +4138,282 @@ } class G1FreeCollectionSetTask : public AbstractGangTask { -private: - - // Closure applied to all regions in the collection set to do work that needs to - // be done serially in a single thread. - class G1SerialFreeCollectionSetClosure : public HeapRegionClosure { - private: - G1EvacuationInfo* _evacuation_info; - const size_t* _surviving_young_words; - - // Bytes used in successfully evacuated regions before the evacuation. - size_t _before_used_bytes; - // Bytes used in unsucessfully evacuated regions before the evacuation - size_t _after_used_bytes; - - size_t _bytes_allocated_in_old_since_last_gc; - - size_t _failure_used_words; - size_t _failure_waste_words; - - FreeRegionList _local_free_list; + // Helper class to keep statistics for the collection set freeing + class FreeCSetStats { + size_t _before_used_bytes; // Usage in regions successfully evacutate + size_t _after_used_bytes; // Usage in regions failing evacuation + size_t _bytes_allocated_in_old_since_last_gc; // Size of young regions turned into old + size_t _failure_used_words; // Live size in failed regions + size_t _failure_waste_words; // Wasted size in failed regions + size_t _rs_length; // Remembered set size + uint _regions_freed; // Number of regions freed public: - G1SerialFreeCollectionSetClosure(G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words) : - HeapRegionClosure(), - _evacuation_info(evacuation_info), - _surviving_young_words(surviving_young_words), - _before_used_bytes(0), - _after_used_bytes(0), - _bytes_allocated_in_old_since_last_gc(0), - _failure_used_words(0), - _failure_waste_words(0), - _local_free_list("Local Region List for CSet Freeing") { + FreeCSetStats() : + _before_used_bytes(0), + _after_used_bytes(0), + _bytes_allocated_in_old_since_last_gc(0), + _failure_used_words(0), + _failure_waste_words(0), + _rs_length(0), + _regions_freed(0) { } + + void merge_stats(FreeCSetStats* other) { + assert(other != NULL, "invariant"); + _before_used_bytes += other->_before_used_bytes; + _after_used_bytes += other->_after_used_bytes; + _bytes_allocated_in_old_since_last_gc += other->_bytes_allocated_in_old_since_last_gc; + _failure_used_words += other->_failure_used_words; + _failure_waste_words += other->_failure_waste_words; + _rs_length += other->_rs_length; + _regions_freed += other->_regions_freed; + } + + void report(G1CollectedHeap* g1h, G1EvacuationInfo* evacuation_info) { + evacuation_info->set_regions_freed(_regions_freed); + evacuation_info->increment_collectionset_used_after(_after_used_bytes); + + g1h->decrement_summary_bytes(_before_used_bytes); + g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words); + + G1Policy *policy = g1h->policy(); + policy->add_bytes_allocated_in_old_since_last_gc(_bytes_allocated_in_old_since_last_gc); + policy->record_rs_length(_rs_length); + policy->cset_regions_freed(); + } + + void account_failed_region(HeapRegion* r) { + size_t used_words = r->marked_bytes() / HeapWordSize; + _failure_used_words += used_words; + _failure_waste_words += HeapRegion::GrainWords - used_words; + _after_used_bytes += r->used(); + + // When moving a young gen region to old gen, we "allocate" that whole + // region there. This is in addition to any already evacuated objects. + // Notify the policy about that. Old gen regions do not cause an + // additional allocation: both the objects still in the region and the + // ones already moved are accounted for elsewhere. + if (r->is_young()) { + _bytes_allocated_in_old_since_last_gc += HeapRegion::GrainBytes; + } + } + + void account_evacuated_region(HeapRegion* r) { + _before_used_bytes += r->used(); + _regions_freed += 1; + } + + void account_rs_length(HeapRegion* r) { + _rs_length += r->rem_set()->occupied(); } + }; + + // Closure applied to all regions in the collection set. + class FreeCSetClosure : public HeapRegionClosure { + // Helper to send JFR events for regions. + class JFREventForRegion { + EventGCPhaseParallel _event; + public: + JFREventForRegion(HeapRegion* region, uint worker_id) : _event() { + _event.set_gcId(GCId::current()); + _event.set_gcWorkerId(worker_id); + if (region->is_young()) { + _event.set_name(G1GCPhaseTimes::phase_name(G1GCPhaseTimes::YoungFreeCSet)); + } else { + _event.set_name(G1GCPhaseTimes::phase_name(G1GCPhaseTimes::NonYoungFreeCSet)); + } + } + + ~JFREventForRegion() { + _event.commit(); + } + }; + + // Helper to do timing for region work. + class TimerForRegion { + Tickspan& _time; + Ticks _start_time; + public: + TimerForRegion(Tickspan& time) : _time(time), _start_time(Ticks::now()) { } + ~TimerForRegion() { + _time += Ticks::now() - _start_time; + } + }; + + // FreeCSetClosure members + G1CollectedHeap* _g1h; + const size_t* _surviving_young_words; + uint _worker_id; + Tickspan _young_time; + Tickspan _non_young_time; + FreeCSetStats* _stats; + + void assert_in_cset(HeapRegion* r) { + assert(r->young_index_in_cset() != 0 && + (uint)r->young_index_in_cset() <= _g1h->collection_set()->young_region_length(), + "Young index %u is wrong for region %u of type %s with %u young regions", + r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->young_region_length()); + } + + void handle_evacuated_region(HeapRegion* r) { + assert(!r->is_empty(), "Region %u is an empty region in the collection set.", r->hrm_index()); + stats()->account_evacuated_region(r); + + // Free the region and and its remembered set. + _g1h->free_region(r, NULL, false /* skip_remset */, true /* skip_hot_card_cache */, true /* locked */); + } + + void handle_failed_region(HeapRegion* r) { + // Do some allocation statistics accounting. Regions that failed evacuation + // are always made old, so there is no need to update anything in the young + // gen statistics, but we need to update old gen statistics. + stats()->account_failed_region(r); + + // Update the region state due to the failed evacuation. + r->handle_evacuation_failure(); + + // Add region to old set, need to hold lock. + MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); + _g1h->old_set_add(r); + } + + Tickspan& timer_for_region(HeapRegion* r) { + return r->is_young() ? _young_time : _non_young_time; + } + + FreeCSetStats* stats() { + return _stats; + } + public: + FreeCSetClosure(const size_t* surviving_young_words, + uint worker_id, + FreeCSetStats* stats) : + HeapRegionClosure(), + _g1h(G1CollectedHeap::heap()), + _surviving_young_words(surviving_young_words), + _worker_id(worker_id), + _young_time(), + _non_young_time(), + _stats(stats) { } virtual bool do_heap_region(HeapRegion* r) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - - assert(r->in_collection_set(), "Region %u should be in collection set.", r->hrm_index()); - g1h->clear_region_attr(r); + assert(r->in_collection_set(), "Invariant: %u missing from CSet", r->hrm_index()); + JFREventForRegion event(r, _worker_id); + TimerForRegion timer(timer_for_region(r)); + + _g1h->clear_region_attr(r); + stats()->account_rs_length(r); if (r->is_young()) { - assert(r->young_index_in_cset() != 0 && (uint)r->young_index_in_cset() <= g1h->collection_set()->young_region_length(), - "Young index %u is wrong for region %u of type %s with %u young regions", - r->young_index_in_cset(), - r->hrm_index(), - r->get_type_str(), - g1h->collection_set()->young_region_length()); - size_t words_survived = _surviving_young_words[r->young_index_in_cset()]; - r->record_surv_words_in_group(words_survived); - } - - if (!r->evacuation_failed()) { - assert(!r->is_empty(), "Region %u is an empty region in the collection set.", r->hrm_index()); - _before_used_bytes += r->used(); - g1h->free_region(r, - &_local_free_list, - true, /* skip_remset */ - true, /* skip_hot_card_cache */ - true /* locked */); + assert_in_cset(r); + r->record_surv_words_in_group(_surviving_young_words[r->young_index_in_cset()]); } else { - r->uninstall_surv_rate_group(); - r->clear_young_index_in_cset(); - r->set_evacuation_failed(false); - // When moving a young gen region to old gen, we "allocate" that whole region - // there. This is in addition to any already evacuated objects. Notify the - // policy about that. - // Old gen regions do not cause an additional allocation: both the objects - // still in the region and the ones already moved are accounted for elsewhere. - if (r->is_young()) { - _bytes_allocated_in_old_since_last_gc += HeapRegion::GrainBytes; - } - // The region is now considered to be old. - r->set_old(); - // Do some allocation statistics accounting. Regions that failed evacuation - // are always made old, so there is no need to update anything in the young - // gen statistics, but we need to update old gen statistics. - size_t used_words = r->marked_bytes() / HeapWordSize; - - _failure_used_words += used_words; - _failure_waste_words += HeapRegion::GrainWords - used_words; - - g1h->old_set_add(r); - _after_used_bytes += r->used(); + _g1h->hot_card_cache()->reset_card_counts(r); } + + if (r->evacuation_failed()) { + handle_failed_region(r); + } else { + handle_evacuated_region(r); + } + assert(!_g1h->is_on_master_free_list(r), "sanity"); + return false; } - void complete_work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - - _evacuation_info->set_regions_freed(_local_free_list.length()); - _evacuation_info->increment_collectionset_used_after(_after_used_bytes); - - g1h->prepend_to_freelist(&_local_free_list); - g1h->decrement_summary_bytes(_before_used_bytes); - - G1Policy* policy = g1h->policy(); - policy->add_bytes_allocated_in_old_since_last_gc(_bytes_allocated_in_old_since_last_gc); - - g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words); - } - }; - - G1CollectionSet* _collection_set; - G1SerialFreeCollectionSetClosure _cl; - const size_t* _surviving_young_words; - - size_t _rs_length; - - volatile jint _serial_work_claim; - - struct WorkItem { - uint region_idx; - bool is_young; - bool evacuation_failed; - - WorkItem(HeapRegion* r) { - region_idx = r->hrm_index(); - is_young = r->is_young(); - evacuation_failed = r->evacuation_failed(); + void report_timing(Tickspan parallel_time) { + G1GCPhaseTimes* pt = _g1h->phase_times(); + pt->record_time_secs(G1GCPhaseTimes::ParFreeCSet, _worker_id, parallel_time.seconds()); + if (_young_time.value() > 0) { + pt->record_time_secs(G1GCPhaseTimes::YoungFreeCSet, _worker_id, _young_time.seconds()); + } + if (_non_young_time.value() > 0) { + pt->record_time_secs(G1GCPhaseTimes::NonYoungFreeCSet, _worker_id, _non_young_time.seconds()); + } } }; - volatile size_t _parallel_work_claim; - size_t _num_work_items; - WorkItem* _work_items; - - void do_serial_work() { - // Need to grab the lock to be allowed to modify the old region list. - MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); - _collection_set->iterate(&_cl); - } - - void do_parallel_work_for_region(uint region_idx, bool is_young, bool evacuation_failed) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - - HeapRegion* r = g1h->region_at(region_idx); - assert(!g1h->is_on_master_free_list(r), "sanity"); - - Atomic::add(&_rs_length, r->rem_set()->occupied()); - - if (!is_young) { - g1h->hot_card_cache()->reset_card_counts(r); - } - - if (!evacuation_failed) { - r->rem_set()->clear_locked(); - } + // G1FreeCollectionSetTask members + G1CollectedHeap* _g1h; + G1EvacuationInfo* _evacuation_info; + FreeCSetStats* _worker_stats; + HeapRegionClaimer _claimer; + const size_t* _surviving_young_words; + uint _active_workers; + + FreeCSetStats* worker_stats(uint worker) { + return &_worker_stats[worker]; } - class G1PrepareFreeCollectionSetClosure : public HeapRegionClosure { - private: - size_t _cur_idx; - WorkItem* _work_items; - public: - G1PrepareFreeCollectionSetClosure(WorkItem* work_items) : HeapRegionClosure(), _cur_idx(0), _work_items(work_items) { } - - virtual bool do_heap_region(HeapRegion* r) { - _work_items[_cur_idx++] = WorkItem(r); - return false; + void report_statistics() { + // Merge the accounting + FreeCSetStats total_stats; + for (uint worker = 0; worker < _active_workers; worker++) { + total_stats.merge_stats(worker_stats(worker)); } - }; - - void prepare_work() { - G1PrepareFreeCollectionSetClosure cl(_work_items); - _collection_set->iterate(&cl); + total_stats.report(_g1h, _evacuation_info); } - void complete_work() { - _cl.complete_work(); - - G1Policy* policy = G1CollectedHeap::heap()->policy(); - policy->record_rs_length(_rs_length); - policy->cset_regions_freed(); - } public: - G1FreeCollectionSetTask(G1CollectionSet* collection_set, G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words) : - AbstractGangTask("G1 Free Collection Set"), - _collection_set(collection_set), - _cl(evacuation_info, surviving_young_words), - _surviving_young_words(surviving_young_words), - _rs_length(0), - _serial_work_claim(0), - _parallel_work_claim(0), - _num_work_items(collection_set->region_length()), - _work_items(NEW_C_HEAP_ARRAY(WorkItem, _num_work_items, mtGC)) { - prepare_work(); + G1FreeCollectionSetTask(G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words, uint active_workers) : + AbstractGangTask("G1 Free Collection Set"), + _g1h(G1CollectedHeap::heap()), + _evacuation_info(evacuation_info), + _worker_stats(NEW_C_HEAP_ARRAY(FreeCSetStats, active_workers, mtGC)), + _claimer(active_workers), + _surviving_young_words(surviving_young_words), + _active_workers(active_workers) { + for (uint worker = 0; worker < active_workers; worker++) { + ::new (&_worker_stats[worker]) FreeCSetStats(); + } } ~G1FreeCollectionSetTask() { - complete_work(); - FREE_C_HEAP_ARRAY(WorkItem, _work_items); + Ticks serial_time = Ticks::now(); + report_statistics(); + for (uint worker = 0; worker < _active_workers; worker++) { + _worker_stats[worker].~FreeCSetStats(); + } + FREE_C_HEAP_ARRAY(FreeCSetStats, _worker_stats); + _g1h->phase_times()->record_serial_free_cset_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); } - // Chunk size for work distribution. The chosen value has been determined experimentally - // to be a good tradeoff between overhead and achievable parallelism. - static uint chunk_size() { return 32; } - virtual void work(uint worker_id) { - G1GCPhaseTimes* timer = G1CollectedHeap::heap()->phase_times(); - - // Claim serial work. - if (_serial_work_claim == 0) { - jint value = Atomic::add(&_serial_work_claim, 1) - 1; - if (value == 0) { - double serial_time = os::elapsedTime(); - do_serial_work(); - timer->record_serial_free_cset_time_ms((os::elapsedTime() - serial_time) * 1000.0); - } - } - - // Start parallel work. - double young_time = 0.0; - bool has_young_time = false; - double non_young_time = 0.0; - bool has_non_young_time = false; - - while (true) { - size_t end = Atomic::add(&_parallel_work_claim, chunk_size()); - size_t cur = end - chunk_size(); - - if (cur >= _num_work_items) { - break; - } - - EventGCPhaseParallel event; - double start_time = os::elapsedTime(); - - end = MIN2(end, _num_work_items); - - for (; cur < end; cur++) { - bool is_young = _work_items[cur].is_young; - - do_parallel_work_for_region(_work_items[cur].region_idx, is_young, _work_items[cur].evacuation_failed); - - double end_time = os::elapsedTime(); - double time_taken = end_time - start_time; - if (is_young) { - young_time += time_taken; - has_young_time = true; - event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::YoungFreeCSet)); - } else { - non_young_time += time_taken; - has_non_young_time = true; - event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::NonYoungFreeCSet)); - } - start_time = end_time; - } - } - - if (has_young_time) { - timer->record_time_secs(G1GCPhaseTimes::YoungFreeCSet, worker_id, young_time); - } - if (has_non_young_time) { - timer->record_time_secs(G1GCPhaseTimes::NonYoungFreeCSet, worker_id, non_young_time); - } + EventGCPhaseParallel event; + Ticks start = Ticks::now(); + FreeCSetClosure cl(_surviving_young_words, worker_id, worker_stats(worker_id)); + _g1h->collection_set_par_iterate_all(&cl, &_claimer, worker_id); + + // Report the total parallel time along with some more detailed metrics. + cl.report_timing(Ticks::now() - start); + event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::ParFreeCSet)); } }; void G1CollectedHeap::free_collection_set(G1CollectionSet* collection_set, G1EvacuationInfo& evacuation_info, const size_t* surviving_young_words) { _eden.clear(); - double free_cset_start_time = os::elapsedTime(); - + // The free collections set is split up in two tasks, the first + // frees the collection set and records what regions are free, + // and the second one rebuilds the free list. This proved to be + // more efficient than adding a sorted list to another. + + Ticks free_cset_start_time = Ticks::now(); { - uint const num_regions = _collection_set.region_length(); - uint const num_chunks = MAX2(num_regions / G1FreeCollectionSetTask::chunk_size(), 1U); - uint const num_workers = MIN2(workers()->active_workers(), num_chunks); - - G1FreeCollectionSetTask cl(collection_set, &evacuation_info, surviving_young_words); - - log_debug(gc, ergo)("Running %s using %u workers for collection set length %u", - cl.name(), num_workers, num_regions); + uint const num_cs_regions = _collection_set.region_length(); + uint const num_workers = clamp(num_cs_regions, 1u, workers()->active_workers()); + G1FreeCollectionSetTask cl(&evacuation_info, surviving_young_words, num_workers); + + log_debug(gc, ergo)("Running %s using %u workers for collection set length %u (%u)", + cl.name(), num_workers, num_cs_regions, num_regions()); workers()->run_task(&cl, num_workers); } - phase_times()->record_total_free_cset_time_ms((os::elapsedTime() - free_cset_start_time) * 1000.0); + + Ticks free_cset_end_time = Ticks::now(); + phase_times()->record_total_free_cset_time_ms((free_cset_end_time - free_cset_start_time).seconds() * 1000.0); + + // Now rebuild the free region list. + hrm()->rebuild_free_list(workers()); + phase_times()->record_total_rebuild_freelist_time_ms((Ticks::now() - free_cset_end_time).seconds() * 1000.0); collection_set->clear(); }
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -1170,6 +1170,9 @@ // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl); + // Keep alive an object that was loaded with AS_NO_KEEPALIVE. + virtual void keep_alive(oop obj); + // Iterate over heap regions, in address order, terminating the // iteration early if the "do_heap_region" method returns "true". void heap_region_iterate(HeapRegionClosure* blk) const; @@ -1201,6 +1204,11 @@ void heap_region_par_iterate_from_start(HeapRegionClosure* cl, HeapRegionClaimer* hrclaimer) const; + // Iterate over all regions in the collection set in parallel. + void collection_set_par_iterate_all(HeapRegionClosure* cl, + HeapRegionClaimer* hr_claimer, + uint worker_id); + // Iterate over all regions currently in the current collection set. void collection_set_iterate_all(HeapRegionClosure* blk);
--- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -201,6 +201,13 @@ } } +void G1CollectionSet::par_iterate(HeapRegionClosure* cl, + HeapRegionClaimer* hr_claimer, + uint worker_id, + uint total_workers) const { + iterate_part_from(cl, hr_claimer, 0, cur_length(), worker_id, total_workers); +} + void G1CollectionSet::iterate_optional(HeapRegionClosure* cl) const { assert_at_safepoint(); @@ -215,18 +222,25 @@ HeapRegionClaimer* hr_claimer, uint worker_id, uint total_workers) const { - assert_at_safepoint(); + iterate_part_from(cl, hr_claimer, _inc_part_start, increment_length(), worker_id, total_workers); +} - size_t len = increment_length(); - if (len == 0) { +void G1CollectionSet::iterate_part_from(HeapRegionClosure* cl, + HeapRegionClaimer* hr_claimer, + size_t offset, + size_t length, + uint worker_id, + uint total_workers) const { + assert_at_safepoint(); + if (length == 0) { return; } - size_t start_pos = (worker_id * len) / total_workers; + size_t start_pos = (worker_id * length) / total_workers; size_t cur_pos = start_pos; do { - uint region_idx = _collection_set_regions[cur_pos + _inc_part_start]; + uint region_idx = _collection_set_regions[cur_pos + offset]; if (hr_claimer == NULL || hr_claimer->claim_region(region_idx)) { HeapRegion* r = _g1h->region_at(region_idx); bool result = cl->do_heap_region(r); @@ -234,7 +248,7 @@ } cur_pos++; - if (cur_pos == len) { + if (cur_pos == length) { cur_pos = 0; } } while (cur_pos != start_pos);
--- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -254,6 +254,16 @@ // Select the old regions of the initial collection set and determine how many optional // regions we might be able to evacuate in this pause. void finalize_old_part(double time_remaining_ms); + + // Iterate the part of the collection set given by the offset and length applying the given + // HeapRegionClosure. The worker_id will determine where in the part to start the iteration + // to allow for more efficient parallel iteration. + void iterate_part_from(HeapRegionClosure* cl, + HeapRegionClaimer* hr_claimer, + size_t offset, + size_t length, + uint worker_id, + uint total_workers) const; public: G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy); ~G1CollectionSet(); @@ -306,6 +316,10 @@ // Iterate over the entire collection set (all increments calculated so far), applying // the given HeapRegionClosure on all of them. void iterate(HeapRegionClosure* cl) const; + void par_iterate(HeapRegionClosure* cl, + HeapRegionClaimer* hr_claimer, + uint worker_id, + uint total_workers) const; void iterate_optional(HeapRegionClosure* cl) const;
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -131,8 +131,10 @@ _gc_par_phases[RedirtyCards] = new WorkerDataArray<double>("Parallel Redirty (ms):", max_gc_threads); _gc_par_phases[RedirtyCards]->create_thread_work_items("Redirtied Cards:"); + _gc_par_phases[ParFreeCSet] = new WorkerDataArray<double>("Parallel Free Collection Set (ms):", max_gc_threads); _gc_par_phases[YoungFreeCSet] = new WorkerDataArray<double>("Young Free Collection Set (ms):", max_gc_threads); _gc_par_phases[NonYoungFreeCSet] = new WorkerDataArray<double>("Non-Young Free Collection Set (ms):", max_gc_threads); + _gc_par_phases[RebuildFreeList] = new WorkerDataArray<double>("Parallel Rebuild Free List (ms):", max_gc_threads); reset(); } @@ -167,6 +169,8 @@ _recorded_start_new_cset_time_ms = 0.0; _recorded_total_free_cset_time_ms = 0.0; _recorded_serial_free_cset_time_ms = 0.0; + _recorded_total_rebuild_freelist_time_ms = 0.0; + _recorded_serial_rebuild_freelist_time_ms = 0.0; _cur_fast_reclaim_humongous_time_ms = 0.0; _cur_region_register_time = 0.0; _cur_fast_reclaim_humongous_total = 0; @@ -328,11 +332,11 @@ } } -void G1GCPhaseTimes::trace_phase(WorkerDataArray<double>* phase, bool print_sum) const { +void G1GCPhaseTimes::trace_phase(WorkerDataArray<double>* phase, bool print_sum, uint extra_indent) const { LogTarget(Trace, gc, phases) lt; if (lt.is_enabled()) { LogStream ls(lt); - log_phase(phase, 3, &ls, print_sum); + log_phase(phase, 3 + extra_indent, &ls, print_sum); } } @@ -456,6 +460,7 @@ _cur_strong_code_root_purge_time_ms + _recorded_redirty_logged_cards_time_ms + _recorded_total_free_cset_time_ms + + _recorded_total_rebuild_freelist_time_ms + _cur_fast_reclaim_humongous_time_ms + _cur_expand_heap_time_ms + _cur_string_deduplication_time_ms; @@ -492,9 +497,14 @@ #endif debug_time("Free Collection Set", _recorded_total_free_cset_time_ms); - trace_time("Free Collection Set Serial", _recorded_serial_free_cset_time_ms); - trace_phase(_gc_par_phases[YoungFreeCSet]); - trace_phase(_gc_par_phases[NonYoungFreeCSet]); + trace_time("Serial Free Collection Set", _recorded_serial_free_cset_time_ms); + trace_phase(_gc_par_phases[ParFreeCSet]); + trace_phase(_gc_par_phases[YoungFreeCSet], true, 1); + trace_phase(_gc_par_phases[NonYoungFreeCSet], true, 1); + + debug_time("Rebuild Free List", _recorded_total_rebuild_freelist_time_ms); + trace_time("Serial Rebuild Free List ", _recorded_serial_rebuild_freelist_time_ms); + trace_phase(_gc_par_phases[RebuildFreeList]); if (G1EagerReclaimHumongousObjects) { debug_time("Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms); @@ -566,8 +576,10 @@ "StringDedupQueueFixup", "StringDedupTableFixup", "RedirtyCards", + "ParFreeCSet", "YoungFreeCSet", "NonYoungFreeCSet", + "RebuildFreeList", "MergePSS" //GCParPhasesSentinel only used to tell end of enum };
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -76,8 +76,10 @@ StringDedupQueueFixup, StringDedupTableFixup, RedirtyCards, + ParFreeCSet, YoungFreeCSet, NonYoungFreeCSet, + RebuildFreeList, MergePSS, GCParPhasesSentinel }; @@ -171,6 +173,10 @@ double _recorded_serial_free_cset_time_ms; + double _recorded_total_rebuild_freelist_time_ms; + + double _recorded_serial_rebuild_freelist_time_ms; + double _cur_region_register_time; double _cur_fast_reclaim_humongous_time_ms; @@ -195,7 +201,7 @@ void log_phase(WorkerDataArray<double>* phase, uint indent, outputStream* out, bool print_sum) const; void debug_serial_phase(WorkerDataArray<double>* phase, uint extra_indent = 0) const; void debug_phase(WorkerDataArray<double>* phase, uint extra_indent = 0) const; - void trace_phase(WorkerDataArray<double>* phase, bool print_sum = true) const; + void trace_phase(WorkerDataArray<double>* phase, bool print_sum = true, uint extra_indent = 0) const; void info_time(const char* name, double value) const; void debug_time(const char* name, double value) const; @@ -318,6 +324,14 @@ _recorded_serial_free_cset_time_ms = time_ms; } + void record_total_rebuild_freelist_time_ms(double time_ms) { + _recorded_total_rebuild_freelist_time_ms = time_ms; + } + + void record_serial_rebuild_freelist_time_ms(double time_ms) { + _recorded_serial_rebuild_freelist_time_ms = time_ms; + } + void record_register_regions(double time_ms, size_t total, size_t candidates) { _cur_region_register_time = time_ms; _cur_fast_reclaim_humongous_total = total; @@ -401,6 +415,10 @@ return _recorded_total_free_cset_time_ms; } + double total_rebuild_freelist_time_ms() { + return _recorded_total_rebuild_freelist_time_ms; + } + double non_young_cset_choice_time_ms() { return _recorded_non_young_cset_choice_time_ms; }
--- a/src/hotspot/share/gc/g1/g1Policy.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/g1Policy.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -587,7 +587,7 @@ } double G1Policy::constant_other_time_ms(double pause_time_ms) const { - return other_time_ms(pause_time_ms) - phase_times()->total_free_cset_time_ms(); + return other_time_ms(pause_time_ms) - phase_times()->total_free_cset_time_ms() - phase_times()->total_rebuild_freelist_time_ms(); } bool G1Policy::about_to_start_mixed_phase() const {
--- a/src/hotspot/share/gc/g1/heapRegion.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegion.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -110,6 +110,19 @@ } } +void HeapRegion::handle_evacuation_failure() { + uninstall_surv_rate_group(); + clear_young_index_in_cset(); + set_evacuation_failed(false); + set_old(); +} + +void HeapRegion::unlink_from_list() { + set_next(NULL); + set_prev(NULL); + set_containing_set(NULL); +} + void HeapRegion::hr_clear(bool keep_remset, bool clear_space, bool locked) { assert(_humongous_start_region == NULL, "we should have already filtered out humongous regions");
--- a/src/hotspot/share/gc/g1/heapRegion.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegion.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -464,14 +464,16 @@ void set_prev(HeapRegion* prev) { _prev = prev; } HeapRegion* prev() { return _prev; } + void unlink_from_list(); + // Every region added to a set is tagged with a reference to that // set. This is used for doing consistency checking to make sure that // the contents of a set are as they should be and it's only // available in non-product builds. #ifdef ASSERT void set_containing_set(HeapRegionSetBase* containing_set) { - assert((containing_set == NULL && _containing_set != NULL) || - (containing_set != NULL && _containing_set == NULL), + assert((containing_set != NULL && _containing_set == NULL) || + containing_set == NULL, "containing_set: " PTR_FORMAT " " "_containing_set: " PTR_FORMAT, p2i(containing_set), p2i(_containing_set)); @@ -559,6 +561,9 @@ return (HeapWord *) obj >= next_top_at_mark_start(); } + // Update the region state after a failed evacuation. + void handle_evacuation_failure(); + // Iterate over the objects overlapping the given memory region, applying cl // to all references in the region. This is a helper for // G1RemSet::refine_card*, and is tightly coupled with them.
--- a/src/hotspot/share/gc/g1/heapRegionManager.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegionManager.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -614,3 +614,80 @@ uint old_val = Atomic::cmpxchg(&_claims[region_index], Unclaimed, Claimed); return old_val == Unclaimed; } + +class G1RebuildFreeListTask : public AbstractGangTask { + HeapRegionManager* _hrm; + FreeRegionList* _worker_freelists; + uint _worker_chunk_size; + uint _num_workers; + +public: + G1RebuildFreeListTask(HeapRegionManager* hrm, uint num_workers) : + AbstractGangTask("G1 Rebuild Free List Task"), + _hrm(hrm), + _worker_freelists(NEW_C_HEAP_ARRAY(FreeRegionList, num_workers, mtGC)), + _worker_chunk_size((_hrm->max_length() + num_workers - 1) / num_workers), + _num_workers(num_workers) { + for (uint worker = 0; worker < _num_workers; worker++) { + ::new (&_worker_freelists[worker]) FreeRegionList("Appendable Worker Free List"); + } + } + + ~G1RebuildFreeListTask() { + for (uint worker = 0; worker < _num_workers; worker++) { + _worker_freelists[worker].~FreeRegionList(); + } + FREE_C_HEAP_ARRAY(FreeRegionList, _worker_freelists); + } + + FreeRegionList* worker_freelist(uint worker) { + return &_worker_freelists[worker]; + } + + // Each worker creates a free list for a chunk of the heap. The chunks won't + // be overlapping so we don't need to do any claiming. + void work(uint worker_id) { + Ticks start_time = Ticks::now(); + EventGCPhaseParallel event; + + uint start = worker_id * _worker_chunk_size; + uint end = MIN2(start + _worker_chunk_size, _hrm->max_length()); + + // If start is outside the heap, this worker has nothing to do. + if (start > end) { + return; + } + + FreeRegionList *free_list = worker_freelist(worker_id); + for (uint i = start; i < end; i++) { + HeapRegion *region = _hrm->at_or_null(i); + if (region != NULL && region->is_free()) { + // Need to clear old links to allow to be added to new freelist. + region->unlink_from_list(); + free_list->add_to_tail(region); + } + } + + event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::RebuildFreeList)); + G1CollectedHeap::heap()->phase_times()->record_time_secs(G1GCPhaseTimes::RebuildFreeList, worker_id, (Ticks::now() - start_time).seconds()); + } +}; + +void HeapRegionManager::rebuild_free_list(WorkGang* workers) { + // Abandon current free list to allow a rebuild. + _free_list.abandon(); + + uint const num_workers = clamp(max_length(), 1u, workers->active_workers()); + G1RebuildFreeListTask task(this, num_workers); + + log_debug(gc, ergo)("Running %s using %u workers for rebuilding free list of %u (%u) regions", + task.name(), num_workers, num_free_regions(), max_length()); + workers->run_task(&task, num_workers); + + // Link the partial free lists together. + Ticks serial_time = Ticks::now(); + for (uint worker = 0; worker < num_workers; worker++) { + _free_list.append_ordered(task.worker_freelist(worker)); + } + G1CollectedHeap::heap()->phase_times()->record_serial_rebuild_freelist_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); +}
--- a/src/hotspot/share/gc/g1/heapRegionManager.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -172,6 +172,9 @@ // Insert the given region into the free region list. inline void insert_into_free_list(HeapRegion* hr); + // Rebuild the free region list from scratch. + void rebuild_free_list(WorkGang* workers); + // Insert the given region list into the global free region list. void insert_list_into_free_list(FreeRegionList* list) { _free_list.add_ordered(list);
--- a/src/hotspot/share/gc/g1/heapRegionSet.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegionSet.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -90,6 +90,12 @@ _unrealistically_long_length = len; } +void FreeRegionList::abandon() { + check_mt_safety(); + clear(); + verify_optional(); +} + void FreeRegionList::remove_all() { check_mt_safety(); verify_optional(); @@ -112,10 +118,9 @@ verify_optional(); } -void FreeRegionList::add_ordered(FreeRegionList* from_list) { +void FreeRegionList::add_list_common_start(FreeRegionList* from_list) { check_mt_safety(); from_list->check_mt_safety(); - verify_optional(); from_list->verify_optional(); @@ -138,6 +143,47 @@ hr->set_containing_set(this); } #endif // ASSERT +} + +void FreeRegionList::add_list_common_end(FreeRegionList* from_list) { + _length += from_list->length(); + from_list->clear(); + + verify_optional(); + from_list->verify_optional(); +} + +void FreeRegionList::append_ordered(FreeRegionList* from_list) { + add_list_common_start(from_list); + + if (from_list->is_empty()) { + return; + } + + if (is_empty()) { + // Make from_list the current list. + assert_free_region_list(length() == 0 && _tail == NULL, "invariant"); + _head = from_list->_head; + _tail = from_list->_tail; + } else { + // Add the from_list to the end of the current list. + assert(_tail->hrm_index() < from_list->_head->hrm_index(), "Should be sorted %u < %u", + _tail->hrm_index(), from_list->_head->hrm_index()); + + _tail->set_next(from_list->_head); + from_list->_head->set_prev(_tail); + _tail = from_list->_tail; + } + + add_list_common_end(from_list); +} + +void FreeRegionList::add_ordered(FreeRegionList* from_list) { + add_list_common_start(from_list); + + if (from_list->is_empty()) { + return; + } if (is_empty()) { assert_free_region_list(length() == 0 && _tail == NULL, "invariant"); @@ -178,11 +224,7 @@ } } - _length += from_list->length(); - from_list->clear(); - - verify_optional(); - from_list->verify_optional(); + add_list_common_end(from_list); } void FreeRegionList::remove_starting_at(HeapRegion* first, uint num_regions) {
--- a/src/hotspot/share/gc/g1/heapRegionSet.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegionSet.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -180,6 +180,10 @@ inline void increase_length(uint node_index); inline void decrease_length(uint node_index); + // Common checks for adding a list. + void add_list_common_start(FreeRegionList* from_list); + void add_list_common_end(FreeRegionList* from_list); + protected: // See the comment for HeapRegionSetBase::clear() virtual void clear(); @@ -202,6 +206,8 @@ // Assumes that the list is ordered and will preserve that order. The order // is determined by hrm_index. inline void add_ordered(HeapRegion* hr); + // Same restrictions as above, but adds the region last in the list. + inline void add_to_tail(HeapRegion* region_to_add); // Removes from head or tail based on the given argument. HeapRegion* remove_region(bool from_head); @@ -212,10 +218,15 @@ // Merge two ordered lists. The result is also ordered. The order is // determined by hrm_index. void add_ordered(FreeRegionList* from_list); + void append_ordered(FreeRegionList* from_list); // It empties the list by removing all regions from it. void remove_all(); + // Abandon current free list. Requires that all regions in the current list + // are taken care of separately, to allow a rebuild. + void abandon(); + // Remove all (contiguous) regions from first to first + num_regions -1 from // this list. // Num_regions must be > 1.
--- a/src/hotspot/share/gc/g1/heapRegionSet.inline.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/g1/heapRegionSet.inline.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -50,6 +50,26 @@ _length--; } +inline void FreeRegionList::add_to_tail(HeapRegion* region_to_add) { + assert_free_region_list((length() == 0 && _head == NULL && _tail == NULL && _last == NULL) || + (length() > 0 && _head != NULL && _tail != NULL && _tail->hrm_index() < region_to_add->hrm_index()), + "invariant"); + // add() will verify the region and check mt safety. + add(region_to_add); + + if (_head != NULL) { + // Link into list, next is already NULL, no need to set. + region_to_add->set_prev(_tail); + _tail->set_next(region_to_add); + _tail = region_to_add; + } else { + // Empty list, this region is now the list. + _head = region_to_add; + _tail = region_to_add; + } + increase_length(region_to_add->node_index()); +} + inline void FreeRegionList::add_ordered(HeapRegion* hr) { assert_free_region_list((length() == 0 && _head == NULL && _tail == NULL && _last == NULL) || (length() > 0 && _head != NULL && _tail != NULL),
--- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -43,13 +43,16 @@ PhaseGVN& C2ParseAccess::gvn() const { return _kit->gvn(); } bool C2Access::needs_cpu_membar() const { - bool mismatched = (_decorators & C2_MISMATCHED) != 0; + bool mismatched = (_decorators & C2_MISMATCHED) != 0; bool is_unordered = (_decorators & MO_UNORDERED) != 0; + bool anonymous = (_decorators & C2_UNSAFE_ACCESS) != 0; - bool in_heap = (_decorators & IN_HEAP) != 0; + bool in_heap = (_decorators & IN_HEAP) != 0; + bool in_native = (_decorators & IN_NATIVE) != 0; + bool is_mixed = !in_heap && !in_native; - bool is_write = (_decorators & C2_WRITE_ACCESS) != 0; - bool is_read = (_decorators & C2_READ_ACCESS) != 0; + bool is_write = (_decorators & C2_WRITE_ACCESS) != 0; + bool is_read = (_decorators & C2_READ_ACCESS) != 0; bool is_atomic = is_read && is_write; if (is_atomic) { @@ -63,9 +66,11 @@ // the barriers get omitted and the unsafe reference begins to "pollute" // the alias analysis of the rest of the graph, either Compile::can_alias // or Compile::must_alias will throw a diagnostic assert.) - if (!in_heap || !is_unordered || (mismatched && !_addr.type()->isa_aryptr())) { + if (is_mixed || !is_unordered || (mismatched && !_addr.type()->isa_aryptr())) { return true; } + } else { + assert(!is_mixed, "not unsafe"); } return false; @@ -80,7 +85,7 @@ bool requires_atomic_access = (decorators & MO_UNORDERED) == 0; bool in_native = (decorators & IN_NATIVE) != 0; - assert(!in_native, "not supported yet"); + assert(!in_native || (unsafe && !access.is_oop()), "not supported yet"); MemNode::MemOrd mo = access.mem_node_mo();
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -387,6 +387,9 @@ // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl) = 0; + // Keep alive an object that was loaded with AS_NO_KEEPALIVE. + virtual void keep_alive(oop obj) {} + // Returns the longest time (in ms) that has elapsed since the last // time that any part of the heap was examined by a garbage collection. virtual jlong millis_since_last_gc() = 0;
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -1327,6 +1327,11 @@ } } +// Keep alive an object that was loaded with AS_NO_KEEPALIVE. +void ShenandoahHeap::keep_alive(oop obj) { + ShenandoahBarrierSet::barrier_set()->enqueue(obj); +} + void ShenandoahHeap::heap_region_iterate(ShenandoahHeapRegionClosure* blk) const { for (size_t i = 0; i < num_regions(); i++) { ShenandoahHeapRegion* current = get_region(i);
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -560,6 +560,9 @@ // Used for native heap walkers: heap dumpers, mostly void object_iterate(ObjectClosure* cl); + // Keep alive an object that was loaded with AS_NO_KEEPALIVE. + void keep_alive(oop obj); + // Used by RMI jlong millis_since_last_gc();
--- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -140,7 +140,7 @@ public: ShenandoahMarkCLDClosure(OopClosure* cl) : _cl(cl) {} void do_cld(ClassLoaderData* cld) { - cld->oops_do(_cl, true, true); + cld->oops_do(_cl, ClassLoaderData::_claim_strong, true); } }; @@ -152,7 +152,7 @@ ShenandoahRemarkCLDClosure(OopClosure* cl) : _cl(cl) {} void do_cld(ClassLoaderData* cld) { if (cld->has_modified_oops()) { - cld->oops_do(_cl, true, true); + cld->oops_do(_cl, ClassLoaderData::_claim_strong, true); } } };
--- a/src/hotspot/share/gc/z/zBarrier.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zBarrier.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -31,14 +31,6 @@ #include "runtime/safepoint.hpp" #include "utilities/debug.hpp" -bool ZBarrier::during_mark() { - return ZGlobalPhase == ZPhaseMark; -} - -bool ZBarrier::during_relocate() { - return ZGlobalPhase == ZPhaseRelocate; -} - template <bool finalizable> bool ZBarrier::should_mark_through(uintptr_t addr) { // Finalizable marked oops can still exists on the heap after marking
--- a/src/hotspot/share/gc/z/zBarrier.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zBarrier.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -105,6 +105,7 @@ static void keep_alive_barrier_on_weak_oop_field(volatile oop* p); static void keep_alive_barrier_on_phantom_oop_field(volatile oop* p); static void keep_alive_barrier_on_phantom_root_oop_field(oop* p); + static void keep_alive_barrier_on_oop(oop o); // Mark barrier static void mark_barrier_on_oop_field(volatile oop* p, bool finalizable);
--- a/src/hotspot/share/gc/z/zBarrier.inline.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zBarrier.inline.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -144,6 +144,14 @@ return ZAddress::is_weak_good_or_null(addr); } +inline bool ZBarrier::during_mark() { + return ZGlobalPhase == ZPhaseMark; +} + +inline bool ZBarrier::during_relocate() { + return ZGlobalPhase == ZPhaseRelocate; +} + // // Load barrier // @@ -291,6 +299,12 @@ root_barrier<is_good_or_null_fast_path, keep_alive_barrier_on_phantom_oop_slow_path>(p, o); } +inline void ZBarrier::keep_alive_barrier_on_oop(oop o) { + if (during_mark()) { + barrier<is_null_fast_path, mark_barrier_on_oop_slow_path>(NULL, o); + } +} + // // Mark barrier //
--- a/src/hotspot/share/gc/z/zCollectedHeap.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -247,6 +247,10 @@ _heap.object_iterate(cl, true /* visit_weaks */); } +void ZCollectedHeap::keep_alive(oop obj) { + _heap.keep_alive(obj); +} + void ZCollectedHeap::register_nmethod(nmethod* nm) { ZNMethod::register_nmethod(nm); }
--- a/src/hotspot/share/gc/z/zCollectedHeap.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -99,6 +99,8 @@ virtual void object_iterate(ObjectClosure* cl); + virtual void keep_alive(oop obj); + virtual void register_nmethod(nmethod* nm); virtual void unregister_nmethod(nmethod* nm); virtual void flush_nmethod(nmethod* nm);
--- a/src/hotspot/share/gc/z/zHeap.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zHeap.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -322,6 +322,10 @@ return true; } +void ZHeap::keep_alive(oop obj) { + ZBarrier::keep_alive_barrier_on_oop(obj); +} + void ZHeap::set_soft_reference_policy(bool clear) { _reference_processor.set_soft_reference_policy(clear); }
--- a/src/hotspot/share/gc/z/zHeap.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/gc/z/zHeap.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -137,6 +137,7 @@ void mark(bool initial); void mark_flush_and_free(Thread* thread); bool mark_end(); + void keep_alive(oop obj); // Relocation set void select_relocation_set();
--- a/src/hotspot/share/jvmci/jvmciEnv.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -898,12 +898,11 @@ JVMCIObject JVMCIEnv::new_StackTraceElement(const methodHandle& method, int bci, JVMCI_TRAPS) { JavaThread* THREAD = JavaThread::current(); - Symbol* method_name_sym; Symbol* file_name_sym; int line_number; - Handle mirror (THREAD, method->method_holder()->java_mirror()); - java_lang_StackTraceElement::decode(mirror, method, bci, method_name_sym, file_name_sym, line_number); + java_lang_StackTraceElement::decode(method, bci, file_name_sym, line_number, CHECK_(JVMCIObject())); + Symbol* method_name_sym = method->name(); InstanceKlass* holder = method->method_holder(); const char* declaring_class_str = holder->external_name();
--- a/src/hotspot/share/memory/heapInspection.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/memory/heapInspection.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -778,6 +778,10 @@ void do_object(oop obj) { if (obj->is_a(_klass)) { + // obj was read with AS_NO_KEEPALIVE, or equivalent. + // The object needs to be kept alive when it is published. + Universe::heap()->keep_alive(obj); + _result->append(obj); } }
--- a/src/hotspot/share/memory/metaspaceShared.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/memory/metaspaceShared.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -2052,6 +2052,7 @@ if (result == MAP_ARCHIVE_MMAP_FAILURE) { // Mapping has failed (probably due to ASLR). Let's map at an address chosen // by the OS. + log_info(cds)("Try to map archive(s) at an alternative address"); result = map_archives(static_mapinfo, dynamic_mapinfo, false); } }
--- a/src/hotspot/share/oops/accessDecorators.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/oops/accessDecorators.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -176,11 +176,11 @@ ON_PHANTOM_OOP_REF | ON_UNKNOWN_OOP_REF; // === Access Location === -// Accesses can take place in, e.g. the heap, old or young generation and different native roots. +// Accesses can take place in, e.g. the heap, old or young generation, different native roots, or native memory off the heap. // The location is important to the GC as it may imply different actions. The following decorators are used: // * IN_HEAP: The access is performed in the heap. Many barriers such as card marking will // be omitted if this decorator is not set. -// * IN_NATIVE: The access is performed in an off-heap data structure pointing into the Java heap. +// * IN_NATIVE: The access is performed in an off-heap data structure. const DecoratorSet IN_HEAP = UCONST64(1) << 19; const DecoratorSet IN_NATIVE = UCONST64(1) << 20; const DecoratorSet IN_DECORATOR_MASK = IN_HEAP | IN_NATIVE;
--- a/src/hotspot/share/oops/instanceKlass.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/oops/instanceKlass.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -1595,27 +1595,34 @@ bool InstanceKlass::_disable_method_binary_search = false; -int InstanceKlass::quick_search(const Array<Method*>* methods, const Symbol* name) { +NOINLINE int linear_search(const Array<Method*>* methods, const Symbol* name) { int len = methods->length(); int l = 0; int h = len - 1; - + while (l <= h) { + Method* m = methods->at(l); + if (m->name() == name) { + return l; + } + l++; + } + return -1; +} + +inline int InstanceKlass::quick_search(const Array<Method*>* methods, const Symbol* name) { if (_disable_method_binary_search) { + assert(DynamicDumpSharedSpaces, "must be"); // At the final stage of dynamic dumping, the methods array may not be sorted // by ascending addresses of their names, so we can't use binary search anymore. // However, methods with the same name are still laid out consecutively inside the // methods array, so let's look for the first one that matches. - assert(DynamicDumpSharedSpaces, "must be"); - while (l <= h) { - Method* m = methods->at(l); - if (m->name() == name) { - return l; - } - l ++; - } - return -1; + return linear_search(methods, name); } + int len = methods->length(); + int l = 0; + int h = len - 1; + // methods are sorted by ascending addresses of their names, so do binary search while (l <= h) { int mid = (l + h) >> 1;
--- a/src/hotspot/share/oops/instanceKlass.hpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/oops/instanceKlass.hpp Tue Dec 10 10:44:09 2019 +0530 @@ -579,7 +579,7 @@ bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const; private: - static int quick_search(const Array<Method*>* methods, const Symbol* name); + inline static int quick_search(const Array<Method*>* methods, const Symbol* name); public: static void disable_method_binary_search() {
--- a/src/hotspot/share/opto/library_call.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/opto/library_call.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -1087,7 +1087,7 @@ const Type* thread_type = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull); Node* thread = _gvn.transform(new ThreadLocalNode()); Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::threadObj_offset())); - Node* threadObj = make_load(NULL, p, thread_type, T_OBJECT, MemNode::unordered); + Node* threadObj = _gvn.transform(LoadNode::make(_gvn, NULL, immutable_memory(), p, p->bottom_type()->is_ptr(), thread_type, T_OBJECT, MemNode::unordered)); tls_output = thread; return threadObj; } @@ -2447,10 +2447,14 @@ offset = ConvL2X(offset); adr = make_unsafe_address(base, offset, is_store ? ACCESS_WRITE : ACCESS_READ, type, kind == Relaxed); - if (_gvn.type(base)->isa_ptr() != TypePtr::NULL_PTR) { - heap_base_oop = base; - } else if (type == T_OBJECT) { - return false; // off-heap oop accesses are not supported + if (_gvn.type(base)->isa_ptr() == TypePtr::NULL_PTR) { + if (type != T_OBJECT) { + decorators |= IN_NATIVE; // off-heap primitive access + } else { + return false; // off-heap oop accesses are not supported + } + } else { + heap_base_oop = base; // on-heap or mixed access } // Can base be NULL? Otherwise, always on-heap access.
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp Mon Dec 09 14:59:33 2019 -0800 +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp Tue Dec 10 10:44:09 2019 +0530 @@ -483,6 +483,11 @@ // is returned. Otherwise an new entry is allocated. JvmtiTagHashmapEntry* JvmtiTagMap::create_entry(oop ref, jlong tag) { assert(Thread::current()->is_VM_thread() || is_locked(), "checking"); + + // ref was read with AS_NO_KEEPALIVE, or equivalent. + // The object needs to be kept alive when it is published. + Universe::heap()->keep_alive(ref); + JvmtiTagHashmapEntry* entry; if (_free_entries == NULL) { entry = new JvmtiTagHashmapEntry(ref, tag); @@ -2958,7 +2963,7 @@ ClassFieldDescriptor* field = field_map->field_at(i); char type = field->field_type(); if (!is_primitive_field_type(type)) { - oop fld_o = o->obj_field(field->field_offset()); + oop fld_o = o->obj_field_access<AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF>(field->field_offset()); // ignore any objects that aren't visible to profiler if (fld_o != NULL) { assert(Universe::heap()->is_in(fld_o), "unsafe code should not "
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/AdaptiveCoding.java Mon Dec 09 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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 14:59:33 2019 -0800 +++ /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)