diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index 88f7f6e8107..698d3e68ff0 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -32,6 +32,8 @@ inputs: debug-suffix: description: 'File name suffix denoting debug level, possibly empty' required: false + DEPLOY_KEY: + required: true runs: using: composite diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index eae3fd9157b..56ce9d1382e 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -181,4 +181,4 @@ jobs: uses: ./.github/actions/do-build with: make-target: 'hotspot ${{ inputs.make-arguments }}' - platform: linux-${{ matrix.target-cpu }} + platform: linux-${{ matrix.target-cpu }} \ No newline at end of file diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 1f6823c2be4..8c09e76b86a 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -41,7 +41,7 @@ on: debug-levels: required: false type: string - default: '[ "debug", "release" ]' + default: '[ "release" ]' gcc-major-version: required: true type: string @@ -74,10 +74,6 @@ jobs: fail-fast: false matrix: debug-level: ${{ fromJSON(inputs.debug-levels) }} - include: - - debug-level: debug - flags: --with-debug-level=fastdebug - suffix: -debug steps: - name: 'Checkout the JDK source' diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index cbe501ea606..4222b831f8f 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -41,7 +41,7 @@ on: debug-levels: required: false type: string - default: '[ "debug", "release" ]' + default: '[ "release" ]' xcode-toolset-version: required: true type: string @@ -61,10 +61,6 @@ jobs: fail-fast: false matrix: debug-level: ${{ fromJSON(inputs.debug-levels) }} - include: - - debug-level: debug - flags: --with-debug-level=fastdebug - suffix: -debug steps: - name: 'Checkout the JDK source' diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 6a56df295ba..1e97bafcc03 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -41,7 +41,7 @@ on: debug-levels: required: false type: string - default: '[ "debug", "release" ]' + default: '[ "release" ]' msvc-toolset-version: required: true type: string @@ -54,7 +54,6 @@ on: make-arguments: required: false type: string - env: # These are needed to make the MSYS2 bash work properly MSYS2_PATH_TYPE: minimal @@ -72,10 +71,6 @@ jobs: fail-fast: false matrix: debug-level: ${{ fromJSON(inputs.debug-levels) }} - include: - - debug-level: debug - flags: --with-debug-level=fastdebug - suffix: -debug steps: - name: 'Checkout the JDK source' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04cea1293ee..910a7d1c96c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,15 +27,18 @@ name: 'OpenJDK GHA Sanity Checks' on: push: - branches-ignore: - - master - - pr/* + workflow_call: + inputs: + configure-arguments: + description: 'Additional configure arguments' + required: false + type: string + make-arguments: + description: 'Additional make arguments' + required: false + type: string workflow_dispatch: inputs: - platforms: - description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")' - required: true - default: 'linux-x64, linux-x86, linux-x64-variants, linux-cross-compile, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' configure-arguments: description: 'Additional configure arguments' required: false @@ -68,56 +71,18 @@ jobs: docs: ${{ steps.include.outputs.docs }} steps: - # This function must be inlined in main.yml, or we'd be forced to checkout the repo - - name: 'Check what jobs to run' + - name: 'Set what jobs to run' id: include run: | - # Determine which platform jobs to run - - # Returns 'true' if the input platform list matches any of the platform monikers given as argument, - # 'false' otherwise. - # arg $1: platform name or names to look for - function check_platform() { - if [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then - input='${{ github.event.inputs.platforms }}' - elif [[ $GITHUB_EVENT_NAME == push ]]; then - if [[ '${{ !secrets.JDK_SUBMIT_FILTER || startsWith(github.ref, 'refs/heads/submit/') }}' == 'false' ]]; then - # If JDK_SUBMIT_FILTER is set, and this is not a "submit/" branch, don't run anything - >&2 echo 'JDK_SUBMIT_FILTER is set and not a "submit/" branch' - echo 'false' - return - else - input='${{ secrets.JDK_SUBMIT_PLATFORMS }}' - fi - fi - - normalized_input="$(echo ,$input, | tr -d ' ')" - if [[ "$normalized_input" == ",," ]]; then - # For an empty input, assume all platforms should run - echo 'true' - return - else - # Check for all acceptable platform names - for part in $* ; do - if echo "$normalized_input" | grep -q -e ",$part," ; then - echo 'true' - return - fi - done - fi - - echo 'false' - } - - echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT - echo "linux-x86=$(check_platform linux-x86 linux x86)" >> $GITHUB_OUTPUT - echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT - echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT - echo "macos-x64=$(check_platform macos-x64 macos x64)" >> $GITHUB_OUTPUT - echo "macos-aarch64=$(check_platform macos-aarch64 macos aarch64)" >> $GITHUB_OUTPUT - echo "windows-x64=$(check_platform windows-x64 windows x64)" >> $GITHUB_OUTPUT - echo "windows-aarch64=$(check_platform windows-aarch64 windows aarch64)" >> $GITHUB_OUTPUT - echo "docs=$(check_platform docs)" >> $GITHUB_OUTPUT + echo "linux-x64=true" >> $GITHUB_OUTPUT + echo "linux-x86=false" >> $GITHUB_OUTPUT + echo "linux-x64-variants=false" >> $GITHUB_OUTPUT + echo "linux-cross-compile=false" >> $GITHUB_OUTPUT + echo "macos-x64=true" >> $GITHUB_OUTPUT + echo "macos-aarch64=true" >> $GITHUB_OUTPUT + echo "windows-x64=true" >> $GITHUB_OUTPUT + echo "windows-aarch64=false" >> $GITHUB_OUTPUT + echo "docs=false" >> $GITHUB_OUTPUT ### ### Build jobs @@ -130,7 +95,7 @@ jobs: with: platform: linux-x64 gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} # The linux-x64 jdk bundle is used as buildjdk for the cross-compile job @@ -144,7 +109,7 @@ jobs: platform: linux-x86 gcc-major-version: '10' gcc-package-suffix: '-multilib' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' apt-architecture: 'i386' # Some multilib libraries do not have proper inter-dependencies, so we have to # install their dependencies manually. @@ -163,7 +128,7 @@ jobs: make-target: 'hotspot' debug-levels: '[ "debug" ]' gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' extra-conf-options: '--disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -178,7 +143,7 @@ jobs: make-target: 'hotspot' debug-levels: '[ "debug" ]' gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' extra-conf-options: '--with-jvm-variants=zero --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -193,7 +158,7 @@ jobs: make-target: 'hotspot' debug-levels: '[ "debug" ]' gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' extra-conf-options: '--with-jvm-variants=minimal --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -209,7 +174,7 @@ jobs: # Technically this is not the "debug" level, but we can't inject a new matrix state for just this job debug-levels: '[ "debug" ]' gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' extra-conf-options: '--with-debug-level=optimized --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -223,8 +188,8 @@ jobs: uses: ./.github/workflows/build-cross-compile.yml with: gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' - apt-gcc-cross-version: '10.4.0-4ubuntu1~22.04cross1' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' + apt-gcc-cross-version: '10.5.0-1ubuntu1~22.04cross1' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.linux-cross-compile == 'true' @@ -290,7 +255,7 @@ jobs: # build JDK, and we do not need the additional testing of the graphs. extra-conf-options: '--disable-full-docs' gcc-major-version: '10' - apt-gcc-version: '10.4.0-4ubuntu1~22.04' + apt-gcc-version: '10.5.0-1ubuntu1~22.04' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.docs == 'true' @@ -301,6 +266,7 @@ jobs: test-linux-x64: name: linux-x64 + if: false needs: - build-linux-x64 uses: ./.github/workflows/test.yml @@ -311,6 +277,7 @@ jobs: test-linux-x86: name: linux-x86 + if: false needs: - build-linux-x86 uses: ./.github/workflows/test.yml @@ -321,6 +288,7 @@ jobs: test-macos-x64: name: macos-x64 + if: false needs: - build-macos-x64 uses: ./.github/workflows/test.yml @@ -331,6 +299,7 @@ jobs: test-windows-x64: name: windows-x64 + if: false needs: - build-windows-x64 uses: ./.github/workflows/test.yml @@ -343,7 +312,7 @@ jobs: remove-bundles: name: 'Remove bundle artifacts' runs-on: ubuntu-22.04 - if: always() + if: false needs: - build-linux-x64 - build-linux-x86 @@ -385,3 +354,47 @@ jobs: -X DELETE "$url" \ || echo "Failed to remove bundle" done + + upload-bundles: + name: Upload to builds.jabref.org + runs-on: ubuntu-22.04 + needs: + - build-linux-x64 + - build-macos-x64 + - build-macos-aarch64 + - build-windows-x64 + steps: + - name: Get bundles-linux-x64 + uses: actions/download-artifact@v3 + with: + name: bundles-linux-x64 + path: bundles + - name: Get bundles-macos-aarch64 + uses: actions/download-artifact@v3 + with: + name: bundles-macos-aarch64 + path: bundles + - name: Get bundles-macos-x64 + uses: actions/download-artifact@v3 + with: + name: bundles-macos-x64 + path: bundles + - name: Get bundles-windows-x64 + uses: actions/download-artifact@v3 + with: + name: bundles-windows-x64 + path: bundles + - name: Remove obsolete files + run: | + rm bundles/symbols* + rm bundles/tests* + - name: rsync + uses: Pendect/action-rsyncer@v2.0.0 + env: + DEPLOY_KEY: ${{ secrets.BUILDJABREFPRIVATEKEY }} + with: + flags: -vazr --itemize-changes --stats --partial-dir=/tmp/partial + options: '' + ssh_options: '-p 9922' + src: 'bundles/' + dest: jrrsync@build-upload.jabref.org:/var/www/files.jabref.org/www/jdks/ diff --git a/.github/workflows/sync-fork.yml b/.github/workflows/sync-fork.yml new file mode 100644 index 00000000000..e87b62b8be1 --- /dev/null +++ b/.github/workflows/sync-fork.yml @@ -0,0 +1,34 @@ +name: 'Upstream Sync' + +on: + schedule: + - cron: '5 2 * * *' + workflow_dispatch: + +jobs: + sync_latest_from_upstream: + runs-on: ubuntu-latest + name: Sync latest commits from upstream repo + + steps: + - name: Checkout target repo + uses: actions/checkout@v3 + with: + ref: jdk21-fix-8240567 + fetch-depth: '0' + + - name: Sync upstream changes + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Actions" + git remote add upstream https://github.com/openjdk/jdk21.git + git fetch upstream + git merge upstream/master + git push + + build: + # The GitHub trigger "push" does not trigger if a push happens inside a workflow. + # Thus, we manually trigger building + needs: sync_latest_from_upstream + uses: ./.github/workflows/main.yml + secrets: inherit diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java index c01042bdc94..9d0a6c9e5f5 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java @@ -118,6 +118,10 @@ public final class SystemModulesPlugin extends AbstractPlugin { ClassDesc.ofInternalName("jdk/internal/module/SystemModules"); private static final ClassDesc CD_SYSTEM_MODULES_MAP = ClassDesc.ofInternalName(SYSTEM_MODULES_MAP_CLASSNAME); + private static final MethodTypeDesc MTD_StringArray = MethodTypeDesc.of(CD_String.arrayType()); + private static final MethodTypeDesc MTD_SystemModules = MethodTypeDesc.of(CD_SYSTEM_MODULES); + + private int moduleDescriptorsPerMethod = 75; private boolean enabled; public SystemModulesPlugin() { @@ -140,7 +144,14 @@ public boolean hasArguments() { public void configure(Map config) { String arg = config.get(getName()); if (arg != null) { - throw new IllegalArgumentException(getName() + ": " + arg); + String[] split = arg.split("="); + if (split.length != 2) { + throw new IllegalArgumentException(getName() + ": " + arg); + } + if (split[0].equals("batch-size")) { + throw new IllegalArgumentException(getName() + ": " + arg); + } + this.moduleDescriptorsPerMethod = Integer.parseInt(split[1]); } } @@ -316,7 +327,7 @@ private String genSystemModulesClass(List moduleInfos, String className, ResourcePoolBuilder out) { SystemModulesClassGenerator generator - = new SystemModulesClassGenerator(className, moduleInfos); + = new SystemModulesClassGenerator(className, moduleInfos, moduleDescriptorsPerMethod); byte[] bytes = generator.genClassBytes(cf); String rn = "/java.base/" + className + ".class"; ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes); @@ -516,14 +527,27 @@ static class SystemModulesClassGenerator { ClassDesc.ofInternalName("jdk/internal/module/ModuleHashes"); private static final ClassDesc CD_MODULE_RESOLUTION = ClassDesc.ofInternalName("jdk/internal/module/ModuleResolution"); + private static final ClassDesc CD_Map_Entry = ClassDesc.ofInternalName("java/util/Map$Entry"); + private static final MethodTypeDesc MTD_boolean = MethodTypeDesc.of(CD_boolean); + private static final MethodTypeDesc MTD_ModuleDescriptorArray = MethodTypeDesc.of(CD_MODULE_DESCRIPTOR.arrayType()); + private static final MethodTypeDesc MTD_ModuleTargetArray = MethodTypeDesc.of(CD_MODULE_TARGET.arrayType()); + private static final MethodTypeDesc MTD_void_String = MethodTypeDesc.of(CD_void, CD_String); + private static final MethodTypeDesc MTD_void_int = MethodTypeDesc.of(CD_void, CD_int); + private static final MethodTypeDesc MTD_ModuleHashesArray = MethodTypeDesc.of(CD_MODULE_HASHES.arrayType()); + private static final MethodTypeDesc MTD_ModuleResolutionArray = MethodTypeDesc.of(CD_MODULE_RESOLUTION.arrayType()); + private static final MethodTypeDesc MTD_Map = MethodTypeDesc.of(CD_Map); + private static final MethodTypeDesc MTD_MapEntry_Object_Object = MethodTypeDesc.of(CD_Map_Entry, CD_Object, CD_Object); + private static final MethodTypeDesc MTD_Map_MapEntryArray = MethodTypeDesc.of(CD_Map, CD_Map_Entry.arrayType()); + private static final MethodTypeDesc MTD_Set_ObjectArray = MethodTypeDesc.of(CD_Set, CD_Object.arrayType()); private static final int MAX_LOCAL_VARS = 256; - private final int BUILDER_VAR = 0; private final int MD_VAR = 1; // variable for ModuleDescriptor private final int MT_VAR = 1; // variable for ModuleTarget private final int MH_VAR = 1; // variable for ModuleHashes - private int nextLocalVar = 2; // index to next local variable + private final int DEDUP_LIST_VAR = 2; + private final int BUILDER_VAR = 3; + private int nextLocalVar = 4; // index to next local variable // name of class to generate private final ClassDesc classDesc; @@ -531,6 +555,8 @@ static class SystemModulesClassGenerator { // list of all ModuleInfos private final List moduleInfos; + private final int moduleDescriptorsPerMethod; + // A builder to create one single Set instance for a given set of // names or modifiers to reduce the footprint // e.g. target modules of qualified exports @@ -538,9 +564,11 @@ static class SystemModulesClassGenerator { = new DedupSetBuilder(this::getNextLocalVar); public SystemModulesClassGenerator(String className, - List moduleInfos) { + List moduleInfos, + int moduleDescriptorsPerMethod) { this.classDesc = ClassDesc.ofInternalName(className); this.moduleInfos = moduleInfos; + this.moduleDescriptorsPerMethod = moduleDescriptorsPerMethod; moduleInfos.forEach(mi -> dedups(mi.descriptor())); } @@ -616,13 +644,13 @@ public byte[] genClassBytes(Configuration cf) { */ private void genConstructor(ClassBuilder clb) { clb.withMethodBody( - "", - MethodTypeDesc.of(CD_void), + INIT_NAME, + MTD_void, ACC_PUBLIC, cob -> cob.aload(0) .invokespecial(CD_Object, - "", - MethodTypeDesc.of(CD_void)) + INIT_NAME, + MTD_void) .return_()); } @@ -638,7 +666,7 @@ private void genHasSplitPackages(ClassBuilder clb) { clb.withMethodBody( "hasSplitPackages", - MethodTypeDesc.of(CD_boolean), + MTD_boolean, ACC_PUBLIC, cob -> cob.constantInstruction(hasSplitPackages ? 1 : 0) .ireturn()); @@ -656,7 +684,7 @@ private void genIncubatorModules(ClassBuilder clb) { clb.withMethodBody( "hasIncubatorModules", - MethodTypeDesc.of(CD_boolean), + MTD_boolean, ACC_PUBLIC, cob -> cob.constantInstruction(hasIncubatorModules ? 1 : 0) .ireturn()); @@ -666,25 +694,146 @@ private void genIncubatorModules(ClassBuilder clb) { * Generate bytecode for moduleDescriptors method */ private void genModuleDescriptorsMethod(ClassBuilder clb) { + if (moduleInfos.size() <= moduleDescriptorsPerMethod) { + clb.withMethodBody( + "moduleDescriptors", + MTD_ModuleDescriptorArray, + ACC_PUBLIC, + cob -> { + cob.constantInstruction(moduleInfos.size()) + .anewarray(CD_MODULE_DESCRIPTOR) + .astore(MD_VAR); + + for (int index = 0; index < moduleInfos.size(); index++) { + ModuleInfo minfo = moduleInfos.get(index); + new ModuleDescriptorBuilder(cob, + minfo.descriptor(), + minfo.packages(), + index).build(); + } + cob.aload(MD_VAR) + .areturn(); + }); + return; + } + + + // Split the module descriptors be created by multiple helper methods. + // Each helper method "subi" creates the maximum N number of module descriptors + // mi, m{i+1} ... + // to avoid exceeding the 64kb limit of method length. Then it will call + // "sub{i+1}" to creates the next batch of module descriptors m{i+n}, m{i+n+1}... + // and so on. During the construction of the module descriptors, the string sets and + // modifier sets are deduplicated (see SystemModulesClassGenerator.DedupSetBuilder) + // and cached in the locals. These locals are saved in an array list so + // that the helper method can restore the local variables that may be + // referenced by the bytecode generated for creating module descriptors. + // Pseudo code looks like this: + // + // void subi(ModuleDescriptor[] mdescs, ArrayList localvars) { + // // assign localvars to local variables + // var l3 = localvars.get(0); + // var l4 = localvars.get(1); + // : + // // fill mdescs[i] to mdescs[i+n-1] + // mdescs[i] = ... + // mdescs[i+1] = ... + // : + // // save new local variables added + // localvars.add(lx) + // localvars.add(l{x+1}) + // : + // sub{i+i}(mdescs, localvars); + // } + + List> splitModuleInfos = new ArrayList<>(); + List currentModuleInfos = null; + for (int index = 0; index < moduleInfos.size(); index++) { + if (index % moduleDescriptorsPerMethod == 0) { + currentModuleInfos = new ArrayList<>(); + splitModuleInfos.add(currentModuleInfos); + } + currentModuleInfos.add(moduleInfos.get(index)); + } + + String helperMethodNamePrefix = "sub"; + ClassDesc arrayListClassDesc = ClassDesc.ofInternalName("java/util/ArrayList"); + clb.withMethodBody( "moduleDescriptors", - MethodTypeDesc.of(CD_MODULE_DESCRIPTOR.arrayType()), + MTD_ModuleDescriptorArray, ACC_PUBLIC, cob -> { cob.constantInstruction(moduleInfos.size()) .anewarray(CD_MODULE_DESCRIPTOR) + .dup() .astore(MD_VAR); - - for (int index = 0; index < moduleInfos.size(); index++) { - ModuleInfo minfo = moduleInfos.get(index); - new ModuleDescriptorBuilder(cob, - minfo.descriptor(), - minfo.packages(), - index).build(); - } - cob.aload(MD_VAR) + cob.new_(arrayListClassDesc) + .dup() + .constantInstruction(moduleInfos.size()) + .invokespecial(arrayListClassDesc, INIT_NAME, MethodTypeDesc.of(CD_void, CD_int)) + .astore(DEDUP_LIST_VAR); + cob.aload(0) + .aload(MD_VAR) + .aload(DEDUP_LIST_VAR) + .invokevirtual( + this.classDesc, + helperMethodNamePrefix + "0", + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType(), arrayListClassDesc) + ) .areturn(); }); + + int dedupVarStart = nextLocalVar; + for (int n = 0, count = 0; n < splitModuleInfos.size(); count += splitModuleInfos.get(n).size(), n++) { + int index = n; // the index of which ModuleInfo being processed in the current batch + int start = count; // the start index to the return ModuleDescriptor array for the current batch + int curDedupVar = nextLocalVar; + clb.withMethodBody( + helperMethodNamePrefix + index, + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType(), arrayListClassDesc), + ACC_PUBLIC, + cob -> { + if (curDedupVar > dedupVarStart) { + for (int i = dedupVarStart; i < curDedupVar; i++) { + cob.aload(DEDUP_LIST_VAR) + .constantInstruction(i - dedupVarStart) + .invokevirtual(arrayListClassDesc, "get", MethodTypeDesc.of(CD_Object, CD_int)) + .astore(i); + } + } + + List currentBatch = splitModuleInfos.get(index); + for (int j = 0; j < currentBatch.size(); j++) { + ModuleInfo minfo = currentBatch.get(j); + new ModuleDescriptorBuilder(cob, + minfo.descriptor(), + minfo.packages(), + start + j).build(); + } + + if (index < splitModuleInfos.size() - 1) { + if (nextLocalVar > curDedupVar) { + for (int i = curDedupVar; i < nextLocalVar; i++) { + cob.aload(DEDUP_LIST_VAR) + .aload(i) + .invokevirtual(arrayListClassDesc, "add", MethodTypeDesc.of(CD_boolean, CD_Object)) + .pop(); + } + } + cob.aload(0) + .aload(MD_VAR) + .aload(DEDUP_LIST_VAR) + .invokevirtual( + this.classDesc, + helperMethodNamePrefix + (index+1), + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType(), arrayListClassDesc) + ); + } + + cob.return_(); + }); + } } /** @@ -693,7 +842,7 @@ private void genModuleDescriptorsMethod(ClassBuilder clb) { private void genModuleTargetsMethod(ClassBuilder clb) { clb.withMethodBody( "moduleTargets", - MethodTypeDesc.of(CD_MODULE_TARGET.arrayType()), + MTD_ModuleTargetArray, ACC_PUBLIC, cob -> { cob.constantInstruction(moduleInfos.size()) @@ -726,8 +875,8 @@ private void genModuleTargetsMethod(ClassBuilder clb) { .dup() .constantInstruction(minfo.target().targetPlatform()) .invokespecial(CD_MODULE_TARGET, - "", - MethodTypeDesc.of(CD_void, CD_String)); + INIT_NAME, + MTD_void_String); cob.aastore(); } @@ -744,7 +893,7 @@ private void genModuleTargetsMethod(ClassBuilder clb) { private void genModuleHashesMethod(ClassBuilder clb) { clb.withMethodBody( "moduleHashes", - MethodTypeDesc.of(CD_MODULE_HASHES.arrayType()), + MTD_ModuleHashesArray, ACC_PUBLIC, cob -> { cob.constantInstruction(moduleInfos.size()) @@ -771,7 +920,7 @@ private void genModuleHashesMethod(ClassBuilder clb) { private void genModuleResolutionsMethod(ClassBuilder clb) { clb.withMethodBody( "moduleResolutions", - MethodTypeDesc.of(CD_MODULE_RESOLUTION.arrayType()), + MTD_ModuleResolutionArray, ACC_PUBLIC, cob -> { cob.constantInstruction(moduleInfos.size()) @@ -787,8 +936,8 @@ private void genModuleResolutionsMethod(ClassBuilder clb) { .dup() .constantInstruction(minfo.moduleResolution().value()) .invokespecial(CD_MODULE_RESOLUTION, - "", - MethodTypeDesc.of(CD_void, CD_int)) + INIT_NAME, + MTD_void_int) .aastore(); } } @@ -822,7 +971,7 @@ private void generate(ClassBuilder clb, boolean dedup) { clb.withMethodBody( methodName, - MethodTypeDesc.of(CD_Map), + MTD_Map, ACC_PUBLIC, cob -> { @@ -852,7 +1001,7 @@ private void generate(ClassBuilder clb, // new Map$Entry[size] cob.constantInstruction(map.size()) - .anewarray(ClassDesc.ofInternalName("java/util/Map$Entry")); + .anewarray(CD_Map_Entry); int index = 0; for (var e : new TreeMap<>(map).entrySet()) { @@ -871,11 +1020,9 @@ private void generate(ClassBuilder clb, cob.aload(varIndex); } - MethodTypeDesc desc = MethodTypeDesc.ofDescriptor( - "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;"); cob.invokestatic(CD_Map, "entry", - desc, + MTD_MapEntry_Object_Object, true) .aastore(); index++; @@ -884,8 +1031,7 @@ private void generate(ClassBuilder clb, // invoke Map.ofEntries(Map$Entry[]) cob.invokestatic(CD_Map, "ofEntries", - MethodTypeDesc.ofDescriptor( - "([Ljava/util/Map$Entry;)Ljava/util/Map;"), + MTD_Map_MapEntryArray, true) .areturn(); }); @@ -912,8 +1058,7 @@ private void genImmutableSet(CodeBuilder cob, Set set) { } cob.invokestatic(CD_Set, "of", - MethodTypeDesc.ofDescriptor( - "([Ljava/lang/Object;)Ljava/util/Set;"), + MTD_Set_ObjectArray, true); } else { for (String element : sorted(set)) { @@ -968,6 +1113,9 @@ class ModuleDescriptorBuilder { static final MethodTypeDesc MTD_SET = MethodTypeDesc.of(CD_MODULE_BUILDER, CD_Set); static final MethodTypeDesc MTD_STRING = MethodTypeDesc.of(CD_MODULE_BUILDER, CD_String); static final MethodTypeDesc MTD_BOOLEAN = MethodTypeDesc.of(CD_MODULE_BUILDER, CD_boolean); + static final MethodTypeDesc MTD_void_String = MethodTypeDesc.of(CD_void, CD_String); + static final MethodTypeDesc MTD_ModuleDescriptor_int = MethodTypeDesc.of(CD_MODULE_DESCRIPTOR, CD_int); + static final MethodTypeDesc MTD_List_ObjectArray = MethodTypeDesc.of(CD_List, CD_Object.arrayType()); final CodeBuilder cob; final ModuleDescriptor md; @@ -1020,8 +1168,8 @@ void newBuilder() { .dup() .constantInstruction(md.name()) .invokespecial(CD_MODULE_BUILDER, - "", - MethodTypeDesc.of(CD_void, CD_String)) + INIT_NAME, + MTD_void_String) .astore(BUILDER_VAR); if (md.isOpen()) { @@ -1057,7 +1205,7 @@ void putModuleDescriptor() { .constantInstruction(md.hashCode()) .invokevirtual(CD_MODULE_BUILDER, "build", - MethodTypeDesc.of(CD_MODULE_DESCRIPTOR, CD_int)) + MTD_ModuleDescriptor_int) .aastore(); } @@ -1283,8 +1431,7 @@ void newProvides(String service, List providers) { } cob.invokestatic(CD_List, "of", - MethodTypeDesc.ofDescriptor( - "([Ljava/lang/Object;)Ljava/util/List;"), + MTD_List_ObjectArray, true) .invokestatic(CD_MODULE_BUILDER, "newProvides", @@ -1343,6 +1490,8 @@ class ModuleHashesBuilder { ClassDesc.ofInternalName("jdk/internal/module/ModuleHashes$Builder"); static final MethodTypeDesc STRING_BYTE_ARRAY_SIG = MethodTypeDesc.of(MODULE_HASHES_BUILDER, CD_String, CD_byte.arrayType()); + static final MethodTypeDesc MTD_void_String_int = MethodTypeDesc.of(CD_void, CD_String, CD_int); + static final MethodTypeDesc MTD_ModuleHashes = MethodTypeDesc.of(CD_MODULE_HASHES); final ModuleHashes recordedHashes; final CodeBuilder cob; @@ -1385,8 +1534,8 @@ void newModuleHashesBuilder() { .constantInstruction(recordedHashes.algorithm()) .constantInstruction(((4 * recordedHashes.names().size()) / 3) + 1) .invokespecial(MODULE_HASHES_BUILDER, - "", - MethodTypeDesc.of(CD_void, CD_String, CD_int)) + INIT_NAME, + MTD_void_String_int) .astore(BUILDER_VAR) .aload(BUILDER_VAR); } @@ -1402,7 +1551,7 @@ void pushModuleHashes() { .aload(BUILDER_VAR) .invokevirtual(MODULE_HASHES_BUILDER, "build", - MethodTypeDesc.of(CD_MODULE_HASHES)) + MTD_ModuleHashes) .aastore(); } @@ -1551,6 +1700,9 @@ int newStringSet(CodeBuilder cob, Set names) { * it will use a new local variable retrieved from the nextLocalVar */ static class SetBuilder> { + private static final MethodTypeDesc MTD_Set_ObjectArray = MethodTypeDesc.of( + CD_Set, CD_Object.arrayType()); + private final Set elements; private final int defaultVarIndex; private final IntSupplier nextLocalVar; @@ -1632,8 +1784,7 @@ private void generateSetOf(CodeBuilder cob, int index) { } cob.invokestatic(CD_Set, "of", - MethodTypeDesc.ofDescriptor( - "([Ljava/lang/Object;)Ljava/util/Set;"), + MTD_Set_ObjectArray, true); } cob.astore(index); @@ -1683,43 +1834,43 @@ private String genSystemModulesMapClass(ClassDesc allSystemModules, // .withMethodBody( - "", - MethodTypeDesc.of(CD_void), + INIT_NAME, + MTD_void, 0, cob -> cob.aload(0) .invokespecial(CD_Object, - "", - MethodTypeDesc.of(CD_void)) + INIT_NAME, + MTD_void) .return_()) // allSystemModules() .withMethodBody( "allSystemModules", - MethodTypeDesc.of(CD_SYSTEM_MODULES), + MTD_SystemModules, ACC_STATIC, cob -> cob.new_(allSystemModules) .dup() .invokespecial(allSystemModules, - "", - MethodTypeDesc.of(CD_void)) + INIT_NAME, + MTD_void) .areturn()) // defaultSystemModules() .withMethodBody( "defaultSystemModules", - MethodTypeDesc.of(CD_SYSTEM_MODULES), + MTD_SystemModules, ACC_STATIC, cob -> cob.new_(defaultSystemModules) .dup() .invokespecial(defaultSystemModules, - "", - MethodTypeDesc.of(CD_void)) + INIT_NAME, + MTD_void) .areturn()) // moduleNames() .withMethodBody( "moduleNames", - MethodTypeDesc.of(CD_String.arrayType()), + MTD_StringArray, ACC_STATIC, cob -> { cob.constantInstruction(map.size()); @@ -1740,7 +1891,7 @@ private String genSystemModulesMapClass(ClassDesc allSystemModules, // classNames() .withMethodBody( "classNames", - MethodTypeDesc.of(CD_String.arrayType()), + MTD_StringArray, ACC_STATIC, cob -> { cob.constantInstruction(map.size()) diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index e5e8a7e7f4f..a4b780a15c3 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -147,13 +147,16 @@ generate-jli-classes.usage=\ \ correctness add ignore-version=true\n\ \ to override this. -system-modules.argument=retainModuleTarget - -system-modules.description=Fast loading of module descriptors (always enabled) +system-modules.argument=batch-size= sets the batch size of module descriptors\n\ +\ to avoid exceeding the method length limit. The default\n\ +\ batch size is 75. system-modules.usage=\ -\ --system-modules retainModuleTarget\n\ -\ Fast loading of module descriptors (always enabled) +\ --system-modules [batch-size=]\n\ +\ The batch size specifies the maximum number of modules\n\ +\ be handled in one method to workaround if the generated\n\ +\ bytecode exceeds the method size limit. The default\n\ +\ batch size is 75. onoff.argument= diff --git a/test/jdk/tools/jlink/JLink100Modules.java b/test/jdk/tools/jlink/JLink100Modules.java new file mode 100644 index 00000000000..e71206c904f --- /dev/null +++ b/test/jdk/tools/jlink/JLink100Modules.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2022, 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. + * + * 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. + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.StringJoiner; +import java.util.spi.ToolProvider; + +import tests.JImageGenerator; +import tests.JImageGenerator.JLinkTask; + +/* + * @test + * @summary Make sure that 100 modules can be linked using jlink. + * @bug 8240567 + * @library ../lib + * @modules java.base/jdk.internal.jimage + * jdk.jdeps/com.sun.tools.classfile + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin + * jdk.jlink/jdk.tools.jmod + * jdk.jlink/jdk.tools.jimage + * jdk.compiler + * @build tests.* + * @run main/othervm -verbose:gc -Xmx1g -Xlog:init=debug -XX:+UnlockDiagnosticVMOptions -XX:+BytecodeVerificationLocal JLink100Modules + */ +public class JLink100Modules { + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() -> new RuntimeException("javac tool not found")); + private static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> new RuntimeException("jlink tool not found")); + + static void report(String command, String[] args) { + System.out.println(command + " " + String.join(" ", Arrays.asList(args))); + } + + static void javac(String[] args) { + report("javac", args); + JAVAC_TOOL.run(System.out, System.err, args); + } + + static void jlink(String[] args) { + report("jlink", args); + JLINK_TOOL.run(System.out, System.err, args); + } + + public static void main(String[] args) throws Exception { + Path src = Paths.get("bug8240567"); + + StringJoiner mainModuleInfoContent = new StringJoiner(";\n requires ", "module bug8240567x {\n requires ", "\n;}"); + + // create 100 modules. With this naming schema up to 130 seem to work + for (int i = 0; i < 150; i++) { + String name = "module" + i + "x"; + Path moduleDir = Files.createDirectories(src.resolve(name)); + + StringBuilder builder = new StringBuilder("module "); + builder.append(name).append(" {"); + + if (i != 0) { + builder.append("requires module0x;"); + } + + builder.append("}\n"); + Files.writeString(moduleDir.resolve("module-info.java"), builder.toString()); + mainModuleInfoContent.add(name); + } + + // create module reading the generated modules + Path mainModulePath = src.resolve("bug8240567x"); + Files.createDirectories(mainModulePath); + Path mainModuleInfo = mainModulePath.resolve("module-info.java"); + Files.writeString(mainModuleInfo, mainModuleInfoContent.toString()); + + Path mainClassDir = mainModulePath.resolve("testpackage"); + Files.createDirectories(mainClassDir); + + Files.writeString(mainClassDir.resolve("JLink100ModulesTest.java"), """ + package testpackage; + + public class JLink100ModulesTest { + public static void main(String[] args) throws Exception { + System.out.println("JLink100ModulesTest started."); + } + } + """); + + String out = src.resolve("out").toString(); + + javac(new String[]{ + "-d", out, + "--module-source-path", src.toString(), + "--module", "bug8240567x" + }); + + JImageGenerator.getJLinkTask() + .modulePath(out) + .output(src.resolve("out-jlink")) + .addMods("bug8240567x") + .call() + .assertSuccess(); + } +}