1 /* 2 * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.tools.jlink.internal.plugins; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.ByteArrayOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.lang.module.Configuration; 32 import java.lang.module.ModuleDescriptor; 33 import java.lang.module.ModuleDescriptor.Exports; 34 import java.lang.module.ModuleDescriptor.Opens; 35 import java.lang.module.ModuleDescriptor.Provides; 36 import java.lang.module.ModuleDescriptor.Requires; 37 import java.lang.module.ModuleDescriptor.Version; 38 import java.lang.module.ModuleFinder; 39 import java.lang.module.ModuleReader; 40 import java.lang.module.ModuleReference; 41 import java.lang.module.ResolvedModule; 42 import java.net.URI; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.EnumSet; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.LinkedHashMap; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Objects; 53 import java.util.Optional; 54 import java.util.Set; 55 import java.util.TreeMap; 56 import java.util.TreeSet; 57 import java.util.function.IntSupplier; 58 import java.util.function.Supplier; 59 import java.util.stream.Collectors; 60 61 import jdk.internal.module.Checks; 62 import jdk.internal.module.DefaultRoots; 63 import jdk.internal.module.IllegalAccessMaps; 64 import jdk.internal.module.Modules; 65 import jdk.internal.module.ModuleHashes; 66 import jdk.internal.module.ModuleInfo.Attributes; 67 import jdk.internal.module.ModuleInfoExtender; 68 import jdk.internal.module.ModuleReferenceImpl; 69 import jdk.internal.module.ModuleResolution; 70 import jdk.internal.module.ModuleTarget; 71 72 import jdk.internal.org.objectweb.asm.ClassReader; 73 import jdk.internal.org.objectweb.asm.ClassVisitor; 74 import jdk.internal.org.objectweb.asm.ClassWriter; 75 import jdk.internal.org.objectweb.asm.MethodVisitor; 76 import jdk.internal.org.objectweb.asm.ModuleVisitor; 77 import jdk.internal.org.objectweb.asm.Opcodes; 78 import static jdk.internal.org.objectweb.asm.Opcodes.*; 79 80 import jdk.tools.jlink.internal.ModuleSorter; 81 import jdk.tools.jlink.plugin.PluginException; 82 import jdk.tools.jlink.plugin.ResourcePool; 83 import jdk.tools.jlink.plugin.ResourcePoolBuilder; 84 import jdk.tools.jlink.plugin.ResourcePoolEntry; 85 86 /** 87 * Jlink plugin to reconstitute module descriptors and other attributes for system 88 * modules. The plugin generates implementations of SystemModules to avoid parsing 89 * module-info.class files at startup. It also generates SystemModulesMap to return 90 * the SystemModules implementation for a specific initial module. 91 * 92 * As a side effect, the plugin adds the ModulePackages class file attribute to the 93 * module-info.class files that don't have the attribute. 94 * 95 * @see jdk.internal.module.SystemModuleFinders 96 * @see jdk.internal.module.SystemModules 97 */ 98 99 public final class SystemModulesPlugin extends AbstractPlugin { 100 private static final String SYSTEM_MODULES_MAP_CLASS = 101 "jdk/internal/module/SystemModulesMap"; 102 private static final String SYSTEM_MODULES_CLASS_PREFIX = 103 "jdk/internal/module/SystemModules$"; 104 private static final String ALL_SYSTEM_MODULES_CLASS = 105 SYSTEM_MODULES_CLASS_PREFIX + "all"; 106 private static final String DEFAULT_SYSTEM_MODULES_CLASS = 107 SYSTEM_MODULES_CLASS_PREFIX + "default"; 108 109 private boolean enabled; 110 SystemModulesPlugin()111 public SystemModulesPlugin() { 112 super("system-modules"); 113 this.enabled = true; 114 } 115 116 @Override getState()117 public Set<State> getState() { 118 return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL) 119 : EnumSet.of(State.DISABLED); 120 } 121 122 @Override hasArguments()123 public boolean hasArguments() { 124 return true; 125 } 126 127 @Override configure(Map<String, String> config)128 public void configure(Map<String, String> config) { 129 String arg = config.get(getName()); 130 if (arg != null) { 131 throw new IllegalArgumentException(getName() + ": " + arg); 132 } 133 } 134 135 @Override transform(ResourcePool in, ResourcePoolBuilder out)136 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 137 if (!enabled) { 138 throw new PluginException(getName() + " was set"); 139 } 140 141 // validate, transform (if needed), and add the module-info.class files 142 List<ModuleInfo> moduleInfos = transformModuleInfos(in, out); 143 144 // generate and add the SystemModuleMap and SystemModules classes 145 Set<String> generated = genSystemModulesClasses(moduleInfos, out); 146 147 // pass through all other resources 148 in.entries() 149 .filter(data -> !data.path().endsWith("/module-info.class") 150 && !generated.contains(data.path())) 151 .forEach(data -> out.add(data)); 152 153 return out.build(); 154 } 155 156 /** 157 * Validates and transforms the module-info.class files in the modules, adding 158 * the ModulePackages class file attribute if needed. 159 * 160 * @return the list of ModuleInfo objects, the first element is java.base 161 */ transformModuleInfos(ResourcePool in, ResourcePoolBuilder out)162 List<ModuleInfo> transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) { 163 List<ModuleInfo> moduleInfos = new ArrayList<>(); 164 165 // Sort modules in the topological order so that java.base is always first. 166 new ModuleSorter(in.moduleView()).sorted().forEach(module -> { 167 ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow( 168 // automatic modules not supported 169 () -> new PluginException("module-info.class not found for " + 170 module.name() + " module") 171 ); 172 173 assert module.name().equals(data.moduleName()); 174 175 try { 176 byte[] content = data.contentBytes(); 177 Set<String> packages = module.packages(); 178 ModuleInfo moduleInfo = new ModuleInfo(content, packages); 179 180 // link-time validation 181 moduleInfo.validateNames(); 182 183 // check if any exported or open package is not present 184 moduleInfo.validatePackages(); 185 186 // module-info.class may be overridden to add ModulePackages 187 if (moduleInfo.shouldRewrite()) { 188 data = data.copyWithContent(moduleInfo.getBytes()); 189 } 190 moduleInfos.add(moduleInfo); 191 192 // add resource pool entry 193 out.add(data); 194 } catch (IOException e) { 195 throw new PluginException(e); 196 } 197 }); 198 199 return moduleInfos; 200 } 201 202 /** 203 * Generates the SystemModules classes (at least one) and the SystemModulesMap 204 * class to map initial modules to a SystemModules class. 205 * 206 * @return the resource names of the resources added to the pool 207 */ genSystemModulesClasses(List<ModuleInfo> moduleInfos, ResourcePoolBuilder out)208 private Set<String> genSystemModulesClasses(List<ModuleInfo> moduleInfos, 209 ResourcePoolBuilder out) { 210 int moduleCount = moduleInfos.size(); 211 ModuleFinder finder = finderOf(moduleInfos); 212 assert finder.findAll().size() == moduleCount; 213 214 // map of initial module name to SystemModules class name 215 Map<String, String> map = new LinkedHashMap<>(); 216 217 // the names of resources written to the pool 218 Set<String> generated = new HashSet<>(); 219 220 // generate the SystemModules implementation to reconstitute all modules 221 Set<String> allModuleNames = moduleInfos.stream() 222 .map(ModuleInfo::moduleName) 223 .collect(Collectors.toSet()); 224 String rn = genSystemModulesClass(moduleInfos, 225 resolve(finder, allModuleNames), 226 ALL_SYSTEM_MODULES_CLASS, 227 out); 228 generated.add(rn); 229 230 // generate, if needed, a SystemModules class to reconstitute the modules 231 // needed for the case that the initial module is the unnamed module. 232 String defaultSystemModulesClassName; 233 Configuration cf = resolve(finder, DefaultRoots.compute(finder)); 234 if (cf.modules().size() == moduleCount) { 235 // all modules are resolved so no need to generate a class 236 defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS; 237 } else { 238 defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS; 239 rn = genSystemModulesClass(sublist(moduleInfos, cf), 240 cf, 241 defaultSystemModulesClassName, 242 out); 243 generated.add(rn); 244 } 245 246 // Generate a SystemModules class for each module with a main class 247 int suffix = 0; 248 for (ModuleInfo mi : moduleInfos) { 249 if (mi.descriptor().mainClass().isPresent()) { 250 String moduleName = mi.moduleName(); 251 cf = resolve(finder, Set.of(moduleName)); 252 if (cf.modules().size() == moduleCount) { 253 // resolves all modules so no need to generate a class 254 map.put(moduleName, ALL_SYSTEM_MODULES_CLASS); 255 } else { 256 String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++); 257 rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out); 258 map.put(moduleName, cn); 259 generated.add(rn); 260 } 261 } 262 } 263 264 // generate SystemModulesMap 265 rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS, 266 defaultSystemModulesClassName, 267 map, 268 out); 269 generated.add(rn); 270 271 // return the resource names of the generated classes 272 return generated; 273 } 274 275 /** 276 * Resolves a collection of root modules, with service binding, to create 277 * a Configuration for the boot layer. 278 */ resolve(ModuleFinder finder, Set<String> roots)279 private Configuration resolve(ModuleFinder finder, Set<String> roots) { 280 return Modules.newBootLayerConfiguration(finder, roots, null); 281 } 282 283 /** 284 * Returns the list of ModuleInfo objects that correspond to the modules in 285 * the given configuration. 286 */ sublist(List<ModuleInfo> moduleInfos, Configuration cf)287 private List<ModuleInfo> sublist(List<ModuleInfo> moduleInfos, Configuration cf) { 288 Set<String> names = cf.modules() 289 .stream() 290 .map(ResolvedModule::name) 291 .collect(Collectors.toSet()); 292 return moduleInfos.stream() 293 .filter(mi -> names.contains(mi.moduleName())) 294 .collect(Collectors.toList()); 295 } 296 297 /** 298 * Generate a SystemModules implementation class and add it as a resource. 299 * 300 * @return the name of the class resource added to the pool 301 */ genSystemModulesClass(List<ModuleInfo> moduleInfos, Configuration cf, String className, ResourcePoolBuilder out)302 private String genSystemModulesClass(List<ModuleInfo> moduleInfos, 303 Configuration cf, 304 String className, 305 ResourcePoolBuilder out) { 306 SystemModulesClassGenerator generator 307 = new SystemModulesClassGenerator(className, moduleInfos); 308 byte[] bytes = generator.getClassWriter(cf).toByteArray(); 309 String rn = "/java.base/" + className + ".class"; 310 ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes); 311 out.add(e); 312 return rn; 313 } 314 315 static class ModuleInfo { 316 private final ByteArrayInputStream bais; 317 private final Attributes attrs; 318 private final Set<String> packages; 319 private final boolean addModulePackages; 320 private ModuleDescriptor descriptor; // may be different that the original one 321 ModuleInfo(byte[] bytes, Set<String> packages)322 ModuleInfo(byte[] bytes, Set<String> packages) throws IOException { 323 this.bais = new ByteArrayInputStream(bytes); 324 this.packages = packages; 325 this.attrs = jdk.internal.module.ModuleInfo.read(bais, null); 326 327 // If ModulePackages attribute is present, the packages from this 328 // module descriptor returns the packages in that attribute. 329 // If it's not present, ModuleDescriptor::packages only contains 330 // the exported and open packages from module-info.class 331 this.descriptor = attrs.descriptor(); 332 if (descriptor.isAutomatic()) { 333 throw new InternalError("linking automatic module is not supported"); 334 } 335 336 // add ModulePackages attribute if this module contains some packages 337 // and ModulePackages is not present 338 this.addModulePackages = packages.size() > 0 && !hasModulePackages(); 339 } 340 moduleName()341 String moduleName() { 342 return attrs.descriptor().name(); 343 } 344 descriptor()345 ModuleDescriptor descriptor() { 346 return descriptor; 347 } 348 packages()349 Set<String> packages() { 350 return packages; 351 } 352 target()353 ModuleTarget target() { 354 return attrs.target(); 355 } 356 recordedHashes()357 ModuleHashes recordedHashes() { 358 return attrs.recordedHashes(); 359 } 360 moduleResolution()361 ModuleResolution moduleResolution() { 362 return attrs.moduleResolution(); 363 } 364 365 /** 366 * Validates names in ModuleDescriptor 367 */ validateNames()368 void validateNames() { 369 Checks.requireModuleName(descriptor.name()); 370 for (Requires req : descriptor.requires()) { 371 Checks.requireModuleName(req.name()); 372 } 373 for (Exports e : descriptor.exports()) { 374 Checks.requirePackageName(e.source()); 375 if (e.isQualified()) 376 e.targets().forEach(Checks::requireModuleName); 377 } 378 for (Opens opens : descriptor.opens()) { 379 Checks.requirePackageName(opens.source()); 380 if (opens.isQualified()) 381 opens.targets().forEach(Checks::requireModuleName); 382 } 383 for (Provides provides : descriptor.provides()) { 384 Checks.requireServiceTypeName(provides.service()); 385 provides.providers().forEach(Checks::requireServiceProviderName); 386 } 387 for (String service : descriptor.uses()) { 388 Checks.requireServiceTypeName(service); 389 } 390 for (String pn : descriptor.packages()) { 391 Checks.requirePackageName(pn); 392 } 393 for (String pn : packages) { 394 Checks.requirePackageName(pn); 395 } 396 } 397 398 /** 399 * Validates if exported and open packages are present 400 */ validatePackages()401 void validatePackages() { 402 Set<String> nonExistPackages = new TreeSet<>(); 403 descriptor.exports().stream() 404 .map(Exports::source) 405 .filter(pn -> !packages.contains(pn)) 406 .forEach(nonExistPackages::add); 407 408 descriptor.opens().stream() 409 .map(Opens::source) 410 .filter(pn -> !packages.contains(pn)) 411 .forEach(nonExistPackages::add); 412 413 if (!nonExistPackages.isEmpty()) { 414 throw new PluginException("Packages that are exported or open in " 415 + descriptor.name() + " are not present: " + nonExistPackages); 416 } 417 } 418 hasModulePackages()419 boolean hasModulePackages() throws IOException { 420 Set<String> packages = new HashSet<>(); 421 ClassVisitor cv = new ClassVisitor(Opcodes.ASM7) { 422 @Override 423 public ModuleVisitor visitModule(String name, 424 int flags, 425 String version) { 426 return new ModuleVisitor(Opcodes.ASM7) { 427 @Override 428 public void visitPackage(String pn) { 429 packages.add(pn); 430 } 431 }; 432 } 433 }; 434 435 try (InputStream in = getInputStream()) { 436 // parse module-info.class 437 ClassReader cr = new ClassReader(in); 438 cr.accept(cv, 0); 439 return packages.size() > 0; 440 } 441 } 442 443 /** 444 * Returns true if module-info.class should be rewritten to add the 445 * ModulePackages attribute. 446 */ shouldRewrite()447 boolean shouldRewrite() { 448 return addModulePackages; 449 } 450 451 /** 452 * Returns the bytes for the (possibly updated) module-info.class. 453 */ getBytes()454 byte[] getBytes() throws IOException { 455 try (InputStream in = getInputStream()) { 456 if (shouldRewrite()) { 457 ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in); 458 if (addModulePackages) { 459 rewriter.addModulePackages(packages); 460 } 461 // rewritten module descriptor 462 byte[] bytes = rewriter.getBytes(); 463 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 464 this.descriptor = ModuleDescriptor.read(bais); 465 } 466 return bytes; 467 } else { 468 return in.readAllBytes(); 469 } 470 } 471 } 472 473 /* 474 * Returns the input stream of the module-info.class 475 */ getInputStream()476 InputStream getInputStream() { 477 bais.reset(); 478 return bais; 479 } 480 481 class ModuleInfoRewriter extends ByteArrayOutputStream { 482 final ModuleInfoExtender extender; ModuleInfoRewriter(InputStream in)483 ModuleInfoRewriter(InputStream in) { 484 this.extender = ModuleInfoExtender.newExtender(in); 485 } 486 addModulePackages(Set<String> packages)487 void addModulePackages(Set<String> packages) { 488 // Add ModulePackages attribute 489 if (packages.size() > 0) { 490 extender.packages(packages); 491 } 492 } 493 getBytes()494 byte[] getBytes() throws IOException { 495 extender.write(this); 496 return buf; 497 } 498 } 499 } 500 501 /** 502 * Generates a SystemModules class to reconstitute the ModuleDescriptor 503 * and other attributes of system modules. 504 */ 505 static class SystemModulesClassGenerator { 506 private static final String MODULE_DESCRIPTOR_BUILDER = 507 "jdk/internal/module/Builder"; 508 private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE = 509 "[Ljava/lang/module/ModuleDescriptor;"; 510 private static final String REQUIRES_MODIFIER_CLASSNAME = 511 "java/lang/module/ModuleDescriptor$Requires$Modifier"; 512 private static final String EXPORTS_MODIFIER_CLASSNAME = 513 "java/lang/module/ModuleDescriptor$Exports$Modifier"; 514 private static final String OPENS_MODIFIER_CLASSNAME = 515 "java/lang/module/ModuleDescriptor$Opens$Modifier"; 516 private static final String MODULE_TARGET_CLASSNAME = 517 "jdk/internal/module/ModuleTarget"; 518 private static final String MODULE_TARGET_ARRAY_SIGNATURE = 519 "[Ljdk/internal/module/ModuleTarget;"; 520 private static final String MODULE_HASHES_ARRAY_SIGNATURE = 521 "[Ljdk/internal/module/ModuleHashes;"; 522 private static final String MODULE_RESOLUTION_CLASSNAME = 523 "jdk/internal/module/ModuleResolution"; 524 private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE = 525 "[Ljdk/internal/module/ModuleResolution;"; 526 527 private static final int MAX_LOCAL_VARS = 256; 528 529 private final int BUILDER_VAR = 0; 530 private final int MD_VAR = 1; // variable for ModuleDescriptor 531 private final int MT_VAR = 1; // variable for ModuleTarget 532 private final int MH_VAR = 1; // variable for ModuleHashes 533 private int nextLocalVar = 2; // index to next local variable 534 535 // Method visitor for generating the SystemModules::modules() method 536 private MethodVisitor mv; 537 538 // name of class to generate 539 private final String className; 540 541 // list of all ModuleDescriptorBuilders, invoked in turn when building. 542 private final List<ModuleInfo> moduleInfos; 543 544 // A builder to create one single Set instance for a given set of 545 // names or modifiers to reduce the footprint 546 // e.g. target modules of qualified exports 547 private final DedupSetBuilder dedupSetBuilder 548 = new DedupSetBuilder(this::getNextLocalVar); 549 SystemModulesClassGenerator(String className, List<ModuleInfo> moduleInfos)550 public SystemModulesClassGenerator(String className, 551 List<ModuleInfo> moduleInfos) { 552 this.className = className; 553 this.moduleInfos = moduleInfos; 554 moduleInfos.forEach(mi -> dedups(mi.descriptor())); 555 } 556 getNextLocalVar()557 private int getNextLocalVar() { 558 return nextLocalVar++; 559 } 560 561 /* 562 * Adds the given ModuleDescriptor to the system module list. 563 * It performs link-time validation and prepares mapping from various 564 * Sets to SetBuilders to emit an optimized number of sets during build. 565 */ dedups(ModuleDescriptor md)566 private void dedups(ModuleDescriptor md) { 567 // exports 568 for (Exports e : md.exports()) { 569 dedupSetBuilder.stringSet(e.targets()); 570 dedupSetBuilder.exportsModifiers(e.modifiers()); 571 } 572 573 // opens 574 for (Opens opens : md.opens()) { 575 dedupSetBuilder.stringSet(opens.targets()); 576 dedupSetBuilder.opensModifiers(opens.modifiers()); 577 } 578 579 // requires 580 for (Requires r : md.requires()) { 581 dedupSetBuilder.requiresModifiers(r.modifiers()); 582 } 583 584 // uses 585 dedupSetBuilder.stringSet(md.uses()); 586 } 587 588 /** 589 * Generate SystemModules class 590 */ getClassWriter(Configuration cf)591 public ClassWriter getClassWriter(Configuration cf) { 592 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS 593 + ClassWriter.COMPUTE_FRAMES); 594 cw.visit(Opcodes.V1_8, 595 ACC_FINAL+ACC_SUPER, 596 className, 597 null, 598 "java/lang/Object", 599 new String[] { "jdk/internal/module/SystemModules" }); 600 601 // generate <init> 602 genConstructor(cw); 603 604 // generate hasSplitPackages 605 genHasSplitPackages(cw); 606 607 // generate hasIncubatorModules 608 genIncubatorModules(cw); 609 610 // generate moduleDescriptors 611 genModuleDescriptorsMethod(cw); 612 613 // generate moduleTargets 614 genModuleTargetsMethod(cw); 615 616 // generate moduleHashes 617 genModuleHashesMethod(cw); 618 619 // generate moduleResolutions 620 genModuleResolutionsMethod(cw); 621 622 // generate moduleReads 623 genModuleReads(cw, cf); 624 625 // generate concealedPackagesToOpen and exportedPackagesToOpen 626 genXXXPackagesToOpenMethods(cw); 627 628 return cw; 629 } 630 631 /** 632 * Generate byteccode for no-arg constructor 633 */ genConstructor(ClassWriter cw)634 private void genConstructor(ClassWriter cw) { 635 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 636 mv.visitVarInsn(ALOAD, 0); 637 mv.visitMethodInsn(INVOKESPECIAL, 638 "java/lang/Object", 639 "<init>", 640 "()V", 641 false); 642 mv.visitInsn(RETURN); 643 mv.visitMaxs(0, 0); 644 mv.visitEnd(); 645 } 646 647 /** 648 * Generate bytecode for hasSplitPackages method 649 */ genHasSplitPackages(ClassWriter cw)650 private void genHasSplitPackages(ClassWriter cw) { 651 boolean distinct = moduleInfos.stream() 652 .map(ModuleInfo::packages) 653 .flatMap(Set::stream) 654 .allMatch(new HashSet<>()::add); 655 boolean hasSplitPackages = !distinct; 656 657 mv = cw.visitMethod(ACC_PUBLIC, 658 "hasSplitPackages", 659 "()Z", 660 "()Z", 661 null); 662 mv.visitCode(); 663 if (hasSplitPackages) { 664 mv.visitInsn(ICONST_1); 665 } else { 666 mv.visitInsn(ICONST_0); 667 } 668 mv.visitInsn(IRETURN); 669 mv.visitMaxs(0, 0); 670 mv.visitEnd(); 671 } 672 673 /** 674 * Generate bytecode for hasIncubatorModules method 675 */ genIncubatorModules(ClassWriter cw)676 private void genIncubatorModules(ClassWriter cw) { 677 boolean hasIncubatorModules = moduleInfos.stream() 678 .map(ModuleInfo::moduleResolution) 679 .filter(mres -> (mres != null && mres.hasIncubatingWarning())) 680 .findFirst() 681 .isPresent(); 682 683 mv = cw.visitMethod(ACC_PUBLIC, 684 "hasIncubatorModules", 685 "()Z", 686 "()Z", 687 null); 688 mv.visitCode(); 689 if (hasIncubatorModules) { 690 mv.visitInsn(ICONST_1); 691 } else { 692 mv.visitInsn(ICONST_0); 693 } 694 mv.visitInsn(IRETURN); 695 mv.visitMaxs(0, 0); 696 mv.visitEnd(); 697 } 698 699 /** 700 * Generate bytecode for moduleDescriptors method 701 */ genModuleDescriptorsMethod(ClassWriter cw)702 private void genModuleDescriptorsMethod(ClassWriter cw) { 703 this.mv = cw.visitMethod(ACC_PUBLIC, 704 "moduleDescriptors", 705 "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, 706 "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, 707 null); 708 mv.visitCode(); 709 pushInt(mv, moduleInfos.size()); 710 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor"); 711 mv.visitVarInsn(ASTORE, MD_VAR); 712 713 for (int index = 0; index < moduleInfos.size(); index++) { 714 ModuleInfo minfo = moduleInfos.get(index); 715 new ModuleDescriptorBuilder(minfo.descriptor(), 716 minfo.packages(), 717 index).build(); 718 } 719 mv.visitVarInsn(ALOAD, MD_VAR); 720 mv.visitInsn(ARETURN); 721 mv.visitMaxs(0, 0); 722 mv.visitEnd(); 723 } 724 725 /** 726 * Generate bytecode for moduleTargets method 727 */ genModuleTargetsMethod(ClassWriter cw)728 private void genModuleTargetsMethod(ClassWriter cw) { 729 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, 730 "moduleTargets", 731 "()" + MODULE_TARGET_ARRAY_SIGNATURE, 732 "()" + MODULE_TARGET_ARRAY_SIGNATURE, 733 null); 734 mv.visitCode(); 735 pushInt(mv, moduleInfos.size()); 736 mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME); 737 mv.visitVarInsn(ASTORE, MT_VAR); 738 739 740 // if java.base has a ModuleTarget attribute then generate the array 741 // with one element, all other elements will be null. 742 743 ModuleInfo base = moduleInfos.get(0); 744 if (!base.moduleName().equals("java.base")) 745 throw new InternalError("java.base should be first module in list"); 746 ModuleTarget target = base.target(); 747 748 int count; 749 if (target != null && target.targetPlatform() != null) { 750 count = 1; 751 } else { 752 count = moduleInfos.size(); 753 } 754 755 for (int index = 0; index < count; index++) { 756 ModuleInfo minfo = moduleInfos.get(index); 757 if (minfo.target() != null) { 758 mv.visitVarInsn(ALOAD, MT_VAR); 759 pushInt(mv, index); 760 761 // new ModuleTarget(String) 762 mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME); 763 mv.visitInsn(DUP); 764 mv.visitLdcInsn(minfo.target().targetPlatform()); 765 mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME, 766 "<init>", "(Ljava/lang/String;)V", false); 767 768 mv.visitInsn(AASTORE); 769 } 770 } 771 772 mv.visitVarInsn(ALOAD, MT_VAR); 773 mv.visitInsn(ARETURN); 774 mv.visitMaxs(0, 0); 775 mv.visitEnd(); 776 } 777 778 /** 779 * Generate bytecode for moduleHashes method 780 */ genModuleHashesMethod(ClassWriter cw)781 private void genModuleHashesMethod(ClassWriter cw) { 782 MethodVisitor hmv = 783 cw.visitMethod(ACC_PUBLIC, 784 "moduleHashes", 785 "()" + MODULE_HASHES_ARRAY_SIGNATURE, 786 "()" + MODULE_HASHES_ARRAY_SIGNATURE, 787 null); 788 hmv.visitCode(); 789 pushInt(hmv, moduleInfos.size()); 790 hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes"); 791 hmv.visitVarInsn(ASTORE, MH_VAR); 792 793 for (int index = 0; index < moduleInfos.size(); index++) { 794 ModuleInfo minfo = moduleInfos.get(index); 795 if (minfo.recordedHashes() != null) { 796 new ModuleHashesBuilder(minfo.recordedHashes(), 797 index, 798 hmv).build(); 799 } 800 } 801 802 hmv.visitVarInsn(ALOAD, MH_VAR); 803 hmv.visitInsn(ARETURN); 804 hmv.visitMaxs(0, 0); 805 hmv.visitEnd(); 806 } 807 808 /** 809 * Generate bytecode for moduleResolutions method 810 */ genModuleResolutionsMethod(ClassWriter cw)811 private void genModuleResolutionsMethod(ClassWriter cw) { 812 MethodVisitor mresmv = 813 cw.visitMethod(ACC_PUBLIC, 814 "moduleResolutions", 815 "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE, 816 "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE, 817 null); 818 mresmv.visitCode(); 819 pushInt(mresmv, moduleInfos.size()); 820 mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME); 821 mresmv.visitVarInsn(ASTORE, 0); 822 823 for (int index=0; index < moduleInfos.size(); index++) { 824 ModuleInfo minfo = moduleInfos.get(index); 825 if (minfo.moduleResolution() != null) { 826 mresmv.visitVarInsn(ALOAD, 0); 827 pushInt(mresmv, index); 828 mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME); 829 mresmv.visitInsn(DUP); 830 mresmv.visitLdcInsn(minfo.moduleResolution().value()); 831 mresmv.visitMethodInsn(INVOKESPECIAL, 832 MODULE_RESOLUTION_CLASSNAME, 833 "<init>", 834 "(I)V", false); 835 mresmv.visitInsn(AASTORE); 836 } 837 } 838 mresmv.visitVarInsn(ALOAD, 0); 839 mresmv.visitInsn(ARETURN); 840 mresmv.visitMaxs(0, 0); 841 mresmv.visitEnd(); 842 } 843 844 /** 845 * Generate bytecode for moduleReads method 846 */ genModuleReads(ClassWriter cw, Configuration cf)847 private void genModuleReads(ClassWriter cw, Configuration cf) { 848 // module name -> names of modules that it reads 849 Map<String, Set<String>> map = cf.modules().stream() 850 .collect(Collectors.toMap( 851 ResolvedModule::name, 852 m -> m.reads().stream() 853 .map(ResolvedModule::name) 854 .collect(Collectors.toSet()))); 855 generate(cw, "moduleReads", map, true); 856 } 857 858 /** 859 * Generate concealedPackagesToOpen and exportedPackagesToOpen methods. 860 */ genXXXPackagesToOpenMethods(ClassWriter cw)861 private void genXXXPackagesToOpenMethods(ClassWriter cw) { 862 ModuleFinder finder = finderOf(moduleInfos); 863 IllegalAccessMaps maps = IllegalAccessMaps.generate(finder); 864 generate(cw, "concealedPackagesToOpen", maps.concealedPackagesToOpen(), false); 865 generate(cw, "exportedPackagesToOpen", maps.exportedPackagesToOpen(), false); 866 } 867 868 /** 869 * Generate method to return {@code Map<String, Set<String>>}. 870 * 871 * If {@code dedup} is true then the values are de-duplicated. 872 */ generate(ClassWriter cw, String methodName, Map<String, Set<String>> map, boolean dedup)873 private void generate(ClassWriter cw, 874 String methodName, 875 Map<String, Set<String>> map, 876 boolean dedup) { 877 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, 878 methodName, 879 "()Ljava/util/Map;", 880 "()Ljava/util/Map;", 881 null); 882 mv.visitCode(); 883 884 // map of Set -> local 885 Map<Set<String>, Integer> locals; 886 887 // generate code to create the sets that are duplicated 888 if (dedup) { 889 Collection<Set<String>> values = map.values(); 890 Set<Set<String>> duplicateSets = values.stream() 891 .distinct() 892 .filter(s -> Collections.frequency(values, s) > 1) 893 .collect(Collectors.toSet()); 894 locals = new HashMap<>(); 895 int index = 1; 896 for (Set<String> s : duplicateSets) { 897 genImmutableSet(mv, s); 898 mv.visitVarInsn(ASTORE, index); 899 locals.put(s, index); 900 if (++index >= MAX_LOCAL_VARS) { 901 break; 902 } 903 } 904 } else { 905 locals = Map.of(); 906 } 907 908 // new Map$Entry[size] 909 pushInt(mv, map.size()); 910 mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry"); 911 912 int index = 0; 913 for (var e : new TreeMap<>(map).entrySet()) { 914 String name = e.getKey(); 915 Set<String> s = e.getValue(); 916 917 mv.visitInsn(DUP); 918 pushInt(mv, index); 919 mv.visitLdcInsn(name); 920 921 // if de-duplicated then load the local, otherwise generate code 922 Integer varIndex = locals.get(s); 923 if (varIndex == null) { 924 genImmutableSet(mv, s); 925 } else { 926 mv.visitVarInsn(ALOAD, varIndex); 927 } 928 929 String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;"; 930 mv.visitMethodInsn(INVOKESTATIC, 931 "java/util/Map", 932 "entry", 933 desc, 934 true); 935 mv.visitInsn(AASTORE); 936 index++; 937 } 938 939 // invoke Map.ofEntries(Map$Entry[]) 940 mv.visitMethodInsn(INVOKESTATIC, "java/util/Map", "ofEntries", 941 "([Ljava/util/Map$Entry;)Ljava/util/Map;", true); 942 mv.visitInsn(ARETURN); 943 mv.visitMaxs(0, 0); 944 mv.visitEnd(); 945 } 946 947 /** 948 * Generate code to generate an immutable set. 949 */ genImmutableSet(MethodVisitor mv, Set<String> set)950 private void genImmutableSet(MethodVisitor mv, Set<String> set) { 951 int size = set.size(); 952 953 // use Set.of(Object[]) when there are more than 2 elements 954 // use Set.of(Object) or Set.of(Object, Object) when fewer 955 if (size > 2) { 956 pushInt(mv, size); 957 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 958 int i = 0; 959 for (String element : sorted(set)) { 960 mv.visitInsn(DUP); 961 pushInt(mv, i); 962 mv.visitLdcInsn(element); 963 mv.visitInsn(AASTORE); 964 i++; 965 } 966 mv.visitMethodInsn(INVOKESTATIC, 967 "java/util/Set", 968 "of", 969 "([Ljava/lang/Object;)Ljava/util/Set;", 970 true); 971 } else { 972 StringBuilder sb = new StringBuilder("("); 973 for (String element : sorted(set)) { 974 mv.visitLdcInsn(element); 975 sb.append("Ljava/lang/Object;"); 976 } 977 sb.append(")Ljava/util/Set;"); 978 mv.visitMethodInsn(INVOKESTATIC, 979 "java/util/Set", 980 "of", 981 sb.toString(), 982 true); 983 } 984 } 985 986 class ModuleDescriptorBuilder { 987 static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;"; 988 static final String EXPORTS_TYPE = 989 "Ljava/lang/module/ModuleDescriptor$Exports;"; 990 static final String OPENS_TYPE = 991 "Ljava/lang/module/ModuleDescriptor$Opens;"; 992 static final String PROVIDES_TYPE = 993 "Ljava/lang/module/ModuleDescriptor$Provides;"; 994 static final String REQUIRES_TYPE = 995 "Ljava/lang/module/ModuleDescriptor$Requires;"; 996 997 // method signature for static Builder::newExports, newOpens, 998 // newProvides, newRequires methods 999 static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG = 1000 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)" 1001 + EXPORTS_TYPE; 1002 static final String EXPORTS_MODIFIER_SET_STRING_SIG = 1003 "(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE; 1004 static final String OPENS_MODIFIER_SET_STRING_SET_SIG = 1005 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)" 1006 + OPENS_TYPE; 1007 static final String OPENS_MODIFIER_SET_STRING_SIG = 1008 "(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE; 1009 static final String PROVIDES_STRING_LIST_SIG = 1010 "(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE; 1011 static final String REQUIRES_SET_STRING_SIG = 1012 "(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE; 1013 static final String REQUIRES_SET_STRING_STRING_SIG = 1014 "(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE; 1015 1016 // method signature for Builder instance methods that 1017 // return this Builder instance 1018 static final String EXPORTS_ARRAY_SIG = 1019 "([" + EXPORTS_TYPE + ")" + BUILDER_TYPE; 1020 static final String OPENS_ARRAY_SIG = 1021 "([" + OPENS_TYPE + ")" + BUILDER_TYPE; 1022 static final String PROVIDES_ARRAY_SIG = 1023 "([" + PROVIDES_TYPE + ")" + BUILDER_TYPE; 1024 static final String REQUIRES_ARRAY_SIG = 1025 "([" + REQUIRES_TYPE + ")" + BUILDER_TYPE; 1026 static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE; 1027 static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE; 1028 static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE; 1029 1030 final ModuleDescriptor md; 1031 final Set<String> packages; 1032 final int index; 1033 ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index)1034 ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) { 1035 if (md.isAutomatic()) { 1036 throw new InternalError("linking automatic module is not supported"); 1037 } 1038 this.md = md; 1039 this.packages = packages; 1040 this.index = index; 1041 } 1042 build()1043 void build() { 1044 // new jdk.internal.module.Builder 1045 newBuilder(); 1046 1047 // requires 1048 requires(md.requires()); 1049 1050 // exports 1051 exports(md.exports()); 1052 1053 // opens 1054 opens(md.opens()); 1055 1056 // uses 1057 uses(md.uses()); 1058 1059 // provides 1060 provides(md.provides()); 1061 1062 // all packages 1063 packages(packages); 1064 1065 // version 1066 md.version().ifPresent(this::version); 1067 1068 // main class 1069 md.mainClass().ifPresent(this::mainClass); 1070 1071 putModuleDescriptor(); 1072 } 1073 newBuilder()1074 void newBuilder() { 1075 mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER); 1076 mv.visitInsn(DUP); 1077 mv.visitLdcInsn(md.name()); 1078 mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER, 1079 "<init>", "(Ljava/lang/String;)V", false); 1080 mv.visitVarInsn(ASTORE, BUILDER_VAR); 1081 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1082 1083 if (md.isOpen()) { 1084 setModuleBit("open", true); 1085 } 1086 if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) { 1087 setModuleBit("synthetic", true); 1088 } 1089 if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) { 1090 setModuleBit("mandated", true); 1091 } 1092 } 1093 1094 /* 1095 * Invoke Builder.<methodName>(boolean value) 1096 */ setModuleBit(String methodName, boolean value)1097 void setModuleBit(String methodName, boolean value) { 1098 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1099 if (value) { 1100 mv.visitInsn(ICONST_1); 1101 } else { 1102 mv.visitInsn(ICONST_0); 1103 } 1104 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1105 methodName, BOOLEAN_SIG, false); 1106 mv.visitInsn(POP); 1107 } 1108 1109 /* 1110 * Put ModuleDescriptor into the modules array 1111 */ putModuleDescriptor()1112 void putModuleDescriptor() { 1113 mv.visitVarInsn(ALOAD, MD_VAR); 1114 pushInt(mv, index); 1115 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1116 mv.visitLdcInsn(md.hashCode()); 1117 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1118 "build", "(I)Ljava/lang/module/ModuleDescriptor;", 1119 false); 1120 mv.visitInsn(AASTORE); 1121 } 1122 1123 /* 1124 * Call Builder::newRequires to create Requires instances and 1125 * then pass it to the builder by calling: 1126 * Builder.requires(Requires[]) 1127 * 1128 */ requires(Set<Requires> requires)1129 void requires(Set<Requires> requires) { 1130 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1131 pushInt(mv, requires.size()); 1132 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires"); 1133 int arrayIndex = 0; 1134 for (Requires require : sorted(requires)) { 1135 String compiledVersion = null; 1136 if (require.compiledVersion().isPresent()) { 1137 compiledVersion = require.compiledVersion().get().toString(); 1138 } 1139 1140 mv.visitInsn(DUP); // arrayref 1141 pushInt(mv, arrayIndex++); 1142 newRequires(require.modifiers(), require.name(), compiledVersion); 1143 mv.visitInsn(AASTORE); 1144 } 1145 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1146 "requires", REQUIRES_ARRAY_SIG, false); 1147 } 1148 1149 /* 1150 * Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion) 1151 * 1152 * Set<Modifier> mods = ... 1153 * Builder.newRequires(mods, mn, compiledVersion); 1154 */ newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion)1155 void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) { 1156 int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods); 1157 mv.visitVarInsn(ALOAD, varIndex); 1158 mv.visitLdcInsn(name); 1159 if (compiledVersion != null) { 1160 mv.visitLdcInsn(compiledVersion); 1161 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1162 "newRequires", REQUIRES_SET_STRING_STRING_SIG, false); 1163 } else { 1164 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1165 "newRequires", REQUIRES_SET_STRING_SIG, false); 1166 } 1167 } 1168 1169 /* 1170 * Call Builder::newExports to create Exports instances and 1171 * then pass it to the builder by calling: 1172 * Builder.exports(Exports[]) 1173 * 1174 */ exports(Set<Exports> exports)1175 void exports(Set<Exports> exports) { 1176 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1177 pushInt(mv, exports.size()); 1178 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports"); 1179 int arrayIndex = 0; 1180 for (Exports export : sorted(exports)) { 1181 mv.visitInsn(DUP); // arrayref 1182 pushInt(mv, arrayIndex++); 1183 newExports(export.modifiers(), export.source(), export.targets()); 1184 mv.visitInsn(AASTORE); 1185 } 1186 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1187 "exports", EXPORTS_ARRAY_SIG, false); 1188 } 1189 1190 /* 1191 * Invoke 1192 * Builder.newExports(Set<Exports.Modifier> ms, String pn, 1193 * Set<String> targets) 1194 * or 1195 * Builder.newExports(Set<Exports.Modifier> ms, String pn) 1196 * 1197 * Set<String> targets = new HashSet<>(); 1198 * targets.add(t); 1199 * : 1200 * : 1201 * 1202 * Set<Modifier> mods = ... 1203 * Builder.newExports(mods, pn, targets); 1204 */ newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets)1205 void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) { 1206 int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms); 1207 if (!targets.isEmpty()) { 1208 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 1209 mv.visitVarInsn(ALOAD, modifiersSetIndex); 1210 mv.visitLdcInsn(pn); 1211 mv.visitVarInsn(ALOAD, stringSetIndex); 1212 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1213 "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false); 1214 } else { 1215 mv.visitVarInsn(ALOAD, modifiersSetIndex); 1216 mv.visitLdcInsn(pn); 1217 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1218 "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false); 1219 } 1220 } 1221 1222 1223 /** 1224 * Call Builder::newOpens to create Opens instances and 1225 * then pass it to the builder by calling: 1226 * Builder.opens(Opens[]) 1227 */ opens(Set<Opens> opens)1228 void opens(Set<Opens> opens) { 1229 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1230 pushInt(mv, opens.size()); 1231 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens"); 1232 int arrayIndex = 0; 1233 for (Opens open : sorted(opens)) { 1234 mv.visitInsn(DUP); // arrayref 1235 pushInt(mv, arrayIndex++); 1236 newOpens(open.modifiers(), open.source(), open.targets()); 1237 mv.visitInsn(AASTORE); 1238 } 1239 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1240 "opens", OPENS_ARRAY_SIG, false); 1241 } 1242 1243 /* 1244 * Invoke 1245 * Builder.newOpens(Set<Opens.Modifier> ms, String pn, 1246 * Set<String> targets) 1247 * or 1248 * Builder.newOpens(Set<Opens.Modifier> ms, String pn) 1249 * 1250 * Set<String> targets = new HashSet<>(); 1251 * targets.add(t); 1252 * : 1253 * : 1254 * 1255 * Set<Modifier> mods = ... 1256 * Builder.newOpens(mods, pn, targets); 1257 */ newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets)1258 void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) { 1259 int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms); 1260 if (!targets.isEmpty()) { 1261 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 1262 mv.visitVarInsn(ALOAD, modifiersSetIndex); 1263 mv.visitLdcInsn(pn); 1264 mv.visitVarInsn(ALOAD, stringSetIndex); 1265 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1266 "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false); 1267 } else { 1268 mv.visitVarInsn(ALOAD, modifiersSetIndex); 1269 mv.visitLdcInsn(pn); 1270 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1271 "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false); 1272 } 1273 } 1274 1275 /* 1276 * Invoke Builder.uses(Set<String> uses) 1277 */ uses(Set<String> uses)1278 void uses(Set<String> uses) { 1279 int varIndex = dedupSetBuilder.indexOfStringSet(uses); 1280 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1281 mv.visitVarInsn(ALOAD, varIndex); 1282 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1283 "uses", SET_SIG, false); 1284 mv.visitInsn(POP); 1285 } 1286 1287 /* 1288 * Call Builder::newProvides to create Provides instances and 1289 * then pass it to the builder by calling: 1290 * Builder.provides(Provides[] provides) 1291 * 1292 */ provides(Collection<Provides> provides)1293 void provides(Collection<Provides> provides) { 1294 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1295 pushInt(mv, provides.size()); 1296 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides"); 1297 int arrayIndex = 0; 1298 for (Provides provide : sorted(provides)) { 1299 mv.visitInsn(DUP); // arrayref 1300 pushInt(mv, arrayIndex++); 1301 newProvides(provide.service(), provide.providers()); 1302 mv.visitInsn(AASTORE); 1303 } 1304 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1305 "provides", PROVIDES_ARRAY_SIG, false); 1306 } 1307 1308 /* 1309 * Invoke Builder.newProvides(String service, Set<String> providers) 1310 * 1311 * Set<String> providers = new HashSet<>(); 1312 * providers.add(impl); 1313 * : 1314 * : 1315 * Builder.newProvides(service, providers); 1316 */ newProvides(String service, List<String> providers)1317 void newProvides(String service, List<String> providers) { 1318 mv.visitLdcInsn(service); 1319 pushInt(mv, providers.size()); 1320 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1321 int arrayIndex = 0; 1322 for (String provider : providers) { 1323 mv.visitInsn(DUP); // arrayref 1324 pushInt(mv, arrayIndex++); 1325 mv.visitLdcInsn(provider); 1326 mv.visitInsn(AASTORE); 1327 } 1328 mv.visitMethodInsn(INVOKESTATIC, "java/util/List", 1329 "of", "([Ljava/lang/Object;)Ljava/util/List;", true); 1330 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1331 "newProvides", PROVIDES_STRING_LIST_SIG, false); 1332 } 1333 1334 /* 1335 * Invoke Builder.packages(String pn) 1336 */ packages(Set<String> packages)1337 void packages(Set<String> packages) { 1338 int varIndex = dedupSetBuilder.newStringSet(packages); 1339 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1340 mv.visitVarInsn(ALOAD, varIndex); 1341 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1342 "packages", SET_SIG, false); 1343 mv.visitInsn(POP); 1344 } 1345 1346 /* 1347 * Invoke Builder.mainClass(String cn) 1348 */ mainClass(String cn)1349 void mainClass(String cn) { 1350 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1351 mv.visitLdcInsn(cn); 1352 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1353 "mainClass", STRING_SIG, false); 1354 mv.visitInsn(POP); 1355 } 1356 1357 /* 1358 * Invoke Builder.version(Version v); 1359 */ version(Version v)1360 void version(Version v) { 1361 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1362 mv.visitLdcInsn(v.toString()); 1363 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1364 "version", STRING_SIG, false); 1365 mv.visitInsn(POP); 1366 } 1367 invokeBuilderMethod(String methodName, String value)1368 void invokeBuilderMethod(String methodName, String value) { 1369 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1370 mv.visitLdcInsn(value); 1371 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1372 methodName, STRING_SIG, false); 1373 mv.visitInsn(POP); 1374 } 1375 } 1376 1377 class ModuleHashesBuilder { 1378 private static final String MODULE_HASHES_BUILDER = 1379 "jdk/internal/module/ModuleHashes$Builder"; 1380 private static final String MODULE_HASHES_BUILDER_TYPE = 1381 "L" + MODULE_HASHES_BUILDER + ";"; 1382 static final String STRING_BYTE_ARRAY_SIG = 1383 "(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE; 1384 1385 final ModuleHashes recordedHashes; 1386 final MethodVisitor hmv; 1387 final int index; 1388 ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv)1389 ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) { 1390 this.recordedHashes = hashes; 1391 this.hmv = hmv; 1392 this.index = index; 1393 } 1394 1395 /** 1396 * Build ModuleHashes 1397 */ build()1398 void build() { 1399 if (recordedHashes == null) 1400 return; 1401 1402 // new jdk.internal.module.ModuleHashes.Builder 1403 newModuleHashesBuilder(); 1404 1405 // Invoke ModuleHashes.Builder::hashForModule 1406 recordedHashes 1407 .names() 1408 .stream() 1409 .sorted() 1410 .forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn))); 1411 1412 // Put ModuleHashes into the hashes array 1413 pushModuleHashes(); 1414 } 1415 1416 1417 /* 1418 * Create ModuleHashes.Builder instance 1419 */ newModuleHashesBuilder()1420 void newModuleHashesBuilder() { 1421 hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER); 1422 hmv.visitInsn(DUP); 1423 hmv.visitLdcInsn(recordedHashes.algorithm()); 1424 pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1); 1425 hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER, 1426 "<init>", "(Ljava/lang/String;I)V", false); 1427 hmv.visitVarInsn(ASTORE, BUILDER_VAR); 1428 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1429 } 1430 1431 1432 /* 1433 * Invoke ModuleHashes.Builder::build and put the returned 1434 * ModuleHashes to the hashes array 1435 */ pushModuleHashes()1436 void pushModuleHashes() { 1437 hmv.visitVarInsn(ALOAD, MH_VAR); 1438 pushInt(hmv, index); 1439 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1440 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 1441 "build", "()Ljdk/internal/module/ModuleHashes;", 1442 false); 1443 hmv.visitInsn(AASTORE); 1444 } 1445 1446 /* 1447 * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash); 1448 */ hashForModule(String name, byte[] hash)1449 void hashForModule(String name, byte[] hash) { 1450 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1451 hmv.visitLdcInsn(name); 1452 1453 pushInt(hmv, hash.length); 1454 hmv.visitIntInsn(NEWARRAY, T_BYTE); 1455 for (int i = 0; i < hash.length; i++) { 1456 hmv.visitInsn(DUP); // arrayref 1457 pushInt(hmv, i); 1458 hmv.visitIntInsn(BIPUSH, hash[i]); 1459 hmv.visitInsn(BASTORE); 1460 } 1461 1462 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 1463 "hashForModule", STRING_BYTE_ARRAY_SIG, false); 1464 hmv.visitInsn(POP); 1465 } 1466 } 1467 1468 /* 1469 * Wraps set creation, ensuring identical sets are properly deduplicated. 1470 */ 1471 class DedupSetBuilder { 1472 // map Set<String> to a specialized builder to allow them to be 1473 // deduplicated as they are requested 1474 final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>(); 1475 1476 // map Set<Requires.Modifier> to a specialized builder to allow them to be 1477 // deduplicated as they are requested 1478 final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>> 1479 requiresModifiersSets = new HashMap<>(); 1480 1481 // map Set<Exports.Modifier> to a specialized builder to allow them to be 1482 // deduplicated as they are requested 1483 final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>> 1484 exportsModifiersSets = new HashMap<>(); 1485 1486 // map Set<Opens.Modifier> to a specialized builder to allow them to be 1487 // deduplicated as they are requested 1488 final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>> 1489 opensModifiersSets = new HashMap<>(); 1490 1491 private final int stringSetVar; 1492 private final int enumSetVar; 1493 private final IntSupplier localVarSupplier; 1494 DedupSetBuilder(IntSupplier localVarSupplier)1495 DedupSetBuilder(IntSupplier localVarSupplier) { 1496 this.stringSetVar = localVarSupplier.getAsInt(); 1497 this.enumSetVar = localVarSupplier.getAsInt(); 1498 this.localVarSupplier = localVarSupplier; 1499 } 1500 1501 /* 1502 * Add the given set of strings to this builder. 1503 */ stringSet(Set<String> strings)1504 void stringSet(Set<String> strings) { 1505 stringSets.computeIfAbsent(strings, 1506 s -> new SetBuilder<>(s, stringSetVar, localVarSupplier) 1507 ).increment(); 1508 } 1509 1510 /* 1511 * Add the given set of Exports.Modifiers 1512 */ exportsModifiers(Set<Exports.Modifier> mods)1513 void exportsModifiers(Set<Exports.Modifier> mods) { 1514 exportsModifiersSets.computeIfAbsent(mods, s -> 1515 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME, 1516 enumSetVar, localVarSupplier) 1517 ).increment(); 1518 } 1519 1520 /* 1521 * Add the given set of Opens.Modifiers 1522 */ opensModifiers(Set<Opens.Modifier> mods)1523 void opensModifiers(Set<Opens.Modifier> mods) { 1524 opensModifiersSets.computeIfAbsent(mods, s -> 1525 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME, 1526 enumSetVar, localVarSupplier) 1527 ).increment(); 1528 } 1529 1530 /* 1531 * Add the given set of Requires.Modifiers 1532 */ requiresModifiers(Set<Requires.Modifier> mods)1533 void requiresModifiers(Set<Requires.Modifier> mods) { 1534 requiresModifiersSets.computeIfAbsent(mods, s -> 1535 new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME, 1536 enumSetVar, localVarSupplier) 1537 ).increment(); 1538 } 1539 1540 /* 1541 * Retrieve the index to the given set of Strings. Emit code to 1542 * generate it when SetBuilder::build is called. 1543 */ indexOfStringSet(Set<String> names)1544 int indexOfStringSet(Set<String> names) { 1545 return stringSets.get(names).build(); 1546 } 1547 1548 /* 1549 * Retrieve the index to the given set of Exports.Modifier. 1550 * Emit code to generate it when EnumSetBuilder::build is called. 1551 */ indexOfExportsModifiers(Set<Exports.Modifier> mods)1552 int indexOfExportsModifiers(Set<Exports.Modifier> mods) { 1553 return exportsModifiersSets.get(mods).build(); 1554 } 1555 1556 /** 1557 * Retrieve the index to the given set of Opens.Modifier. 1558 * Emit code to generate it when EnumSetBuilder::build is called. 1559 */ indexOfOpensModifiers(Set<Opens.Modifier> mods)1560 int indexOfOpensModifiers(Set<Opens.Modifier> mods) { 1561 return opensModifiersSets.get(mods).build(); 1562 } 1563 1564 1565 /* 1566 * Retrieve the index to the given set of Requires.Modifier. 1567 * Emit code to generate it when EnumSetBuilder::build is called. 1568 */ indexOfRequiresModifiers(Set<Requires.Modifier> mods)1569 int indexOfRequiresModifiers(Set<Requires.Modifier> mods) { 1570 return requiresModifiersSets.get(mods).build(); 1571 } 1572 1573 /* 1574 * Build a new string set without any attempt to deduplicate it. 1575 */ newStringSet(Set<String> names)1576 int newStringSet(Set<String> names) { 1577 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build(); 1578 assert index == stringSetVar; 1579 return index; 1580 } 1581 } 1582 1583 /* 1584 * SetBuilder generates bytecode to create one single instance of Set 1585 * for a given set of elements and assign to a local variable slot. 1586 * When there is only one single reference to a Set<T>, 1587 * it will reuse defaultVarIndex. For a Set with multiple references, 1588 * it will use a new local variable retrieved from the nextLocalVar 1589 */ 1590 class SetBuilder<T extends Comparable<T>> { 1591 private final Set<T> elements; 1592 private final int defaultVarIndex; 1593 private final IntSupplier nextLocalVar; 1594 private int refCount; 1595 private int localVarIndex; 1596 SetBuilder(Set<T> elements, int defaultVarIndex, IntSupplier nextLocalVar)1597 SetBuilder(Set<T> elements, 1598 int defaultVarIndex, 1599 IntSupplier nextLocalVar) { 1600 this.elements = elements; 1601 this.defaultVarIndex = defaultVarIndex; 1602 this.nextLocalVar = nextLocalVar; 1603 } 1604 1605 /* 1606 * Increments the number of references to this particular set. 1607 */ increment()1608 final void increment() { 1609 refCount++; 1610 } 1611 1612 /** 1613 * Generate the appropriate instructions to load an object reference 1614 * to the element onto the stack. 1615 */ visitElement(T element, MethodVisitor mv)1616 void visitElement(T element, MethodVisitor mv) { 1617 mv.visitLdcInsn(element); 1618 } 1619 1620 /* 1621 * Build bytecode for the Set represented by this builder, 1622 * or get the local variable index of a previously generated set 1623 * (in the local scope). 1624 * 1625 * @return local variable index of the generated set. 1626 */ build()1627 final int build() { 1628 int index = localVarIndex; 1629 if (localVarIndex == 0) { 1630 // if non-empty and more than one set reference this builder, 1631 // emit to a unique local 1632 index = refCount <= 1 ? defaultVarIndex 1633 : nextLocalVar.getAsInt(); 1634 if (index < MAX_LOCAL_VARS) { 1635 localVarIndex = index; 1636 } else { 1637 // overflow: disable optimization by using localVarIndex = 0 1638 index = defaultVarIndex; 1639 } 1640 1641 generateSetOf(index); 1642 } 1643 return index; 1644 } 1645 generateSetOf(int index)1646 private void generateSetOf(int index) { 1647 if (elements.size() <= 10) { 1648 // call Set.of(e1, e2, ...) 1649 StringBuilder sb = new StringBuilder("("); 1650 for (T t : sorted(elements)) { 1651 sb.append("Ljava/lang/Object;"); 1652 visitElement(t, mv); 1653 } 1654 sb.append(")Ljava/util/Set;"); 1655 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1656 "of", sb.toString(), true); 1657 } else { 1658 // call Set.of(E... elements) 1659 pushInt(mv, elements.size()); 1660 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1661 int arrayIndex = 0; 1662 for (T t : sorted(elements)) { 1663 mv.visitInsn(DUP); // arrayref 1664 pushInt(mv, arrayIndex); 1665 visitElement(t, mv); // value 1666 mv.visitInsn(AASTORE); 1667 arrayIndex++; 1668 } 1669 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1670 "of", "([Ljava/lang/Object;)Ljava/util/Set;", true); 1671 } 1672 mv.visitVarInsn(ASTORE, index); 1673 } 1674 } 1675 1676 /* 1677 * Generates bytecode to create one single instance of EnumSet 1678 * for a given set of modifiers and assign to a local variable slot. 1679 */ 1680 class EnumSetBuilder<T extends Comparable<T>> extends SetBuilder<T> { 1681 1682 private final String className; 1683 EnumSetBuilder(Set<T> modifiers, String className, int defaultVarIndex, IntSupplier nextLocalVar)1684 EnumSetBuilder(Set<T> modifiers, String className, 1685 int defaultVarIndex, 1686 IntSupplier nextLocalVar) { 1687 super(modifiers, defaultVarIndex, nextLocalVar); 1688 this.className = className; 1689 } 1690 1691 /** 1692 * Loads an Enum field. 1693 */ 1694 @Override visitElement(T t, MethodVisitor mv)1695 void visitElement(T t, MethodVisitor mv) { 1696 mv.visitFieldInsn(GETSTATIC, className, t.toString(), 1697 "L" + className + ";"); 1698 } 1699 } 1700 } 1701 1702 /** 1703 * Generate SystemModulesMap and add it as a resource. 1704 * 1705 * @return the name of the class resource added to the pool 1706 */ genSystemModulesMapClass(String allSystemModulesClassName, String defaultSystemModulesClassName, Map<String, String> map, ResourcePoolBuilder out)1707 private String genSystemModulesMapClass(String allSystemModulesClassName, 1708 String defaultSystemModulesClassName, 1709 Map<String, String> map, 1710 ResourcePoolBuilder out) { 1711 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS 1712 + ClassWriter.COMPUTE_FRAMES); 1713 cw.visit(Opcodes.V1_8, 1714 ACC_FINAL+ACC_SUPER, 1715 SYSTEM_MODULES_MAP_CLASS, 1716 null, 1717 "java/lang/Object", 1718 null); 1719 1720 // <init> 1721 MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null); 1722 mv.visitVarInsn(ALOAD, 0); 1723 mv.visitMethodInsn(INVOKESPECIAL, 1724 "java/lang/Object", 1725 "<init>", 1726 "()V", 1727 false); 1728 mv.visitInsn(RETURN); 1729 mv.visitMaxs(0, 0); 1730 mv.visitEnd(); 1731 1732 // allSystemModules() 1733 mv = cw.visitMethod(ACC_STATIC, 1734 "allSystemModules", 1735 "()Ljdk/internal/module/SystemModules;", 1736 "()Ljdk/internal/module/SystemModules;", 1737 null); 1738 mv.visitCode(); 1739 mv.visitTypeInsn(NEW, allSystemModulesClassName); 1740 mv.visitInsn(DUP); 1741 mv.visitMethodInsn(INVOKESPECIAL, 1742 allSystemModulesClassName, 1743 "<init>", 1744 "()V", 1745 false); 1746 mv.visitInsn(ARETURN); 1747 mv.visitMaxs(0, 0); 1748 mv.visitEnd(); 1749 1750 // defaultSystemModules() 1751 mv = cw.visitMethod(ACC_STATIC, 1752 "defaultSystemModules", 1753 "()Ljdk/internal/module/SystemModules;", 1754 "()Ljdk/internal/module/SystemModules;", 1755 null); 1756 mv.visitCode(); 1757 mv.visitTypeInsn(NEW, defaultSystemModulesClassName); 1758 mv.visitInsn(DUP); 1759 mv.visitMethodInsn(INVOKESPECIAL, 1760 defaultSystemModulesClassName, 1761 "<init>", 1762 "()V", 1763 false); 1764 mv.visitInsn(ARETURN); 1765 mv.visitMaxs(0, 0); 1766 mv.visitEnd(); 1767 1768 // moduleNames() 1769 mv = cw.visitMethod(ACC_STATIC, 1770 "moduleNames", 1771 "()[Ljava/lang/String;", 1772 "()[Ljava/lang/String;", 1773 null); 1774 mv.visitCode(); 1775 pushInt(mv, map.size()); 1776 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1777 1778 int index = 0; 1779 for (String moduleName : sorted(map.keySet())) { 1780 mv.visitInsn(DUP); // arrayref 1781 pushInt(mv, index); 1782 mv.visitLdcInsn(moduleName); 1783 mv.visitInsn(AASTORE); 1784 index++; 1785 } 1786 1787 mv.visitInsn(ARETURN); 1788 mv.visitMaxs(0, 0); 1789 mv.visitEnd(); 1790 1791 // classNames() 1792 mv = cw.visitMethod(ACC_STATIC, 1793 "classNames", 1794 "()[Ljava/lang/String;", 1795 "()[Ljava/lang/String;", 1796 null); 1797 mv.visitCode(); 1798 pushInt(mv, map.size()); 1799 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1800 1801 index = 0; 1802 for (String className : sorted(map.values())) { 1803 mv.visitInsn(DUP); // arrayref 1804 pushInt(mv, index); 1805 mv.visitLdcInsn(className.replace('/', '.')); 1806 mv.visitInsn(AASTORE); 1807 index++; 1808 } 1809 1810 mv.visitInsn(ARETURN); 1811 mv.visitMaxs(0, 0); 1812 mv.visitEnd(); 1813 1814 // write the class file to the pool as a resource 1815 String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class"; 1816 ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray()); 1817 out.add(e); 1818 1819 return rn; 1820 } 1821 1822 /** 1823 * Returns a sorted copy of a collection. 1824 * 1825 * This is useful to ensure a deterministic iteration order. 1826 * 1827 * @return a sorted copy of the given collection. 1828 */ sorted(Collection<T> c)1829 private static <T extends Comparable<T>> List<T> sorted(Collection<T> c) { 1830 var l = new ArrayList<T>(c); 1831 Collections.sort(l); 1832 return l; 1833 } 1834 1835 /** 1836 * Pushes an int constant 1837 */ pushInt(MethodVisitor mv, int value)1838 private static void pushInt(MethodVisitor mv, int value) { 1839 if (value <= 5) { 1840 mv.visitInsn(ICONST_0 + value); 1841 } else if (value < Byte.MAX_VALUE) { 1842 mv.visitIntInsn(BIPUSH, value); 1843 } else if (value < Short.MAX_VALUE) { 1844 mv.visitIntInsn(SIPUSH, value); 1845 } else { 1846 throw new IllegalArgumentException("exceed limit: " + value); 1847 } 1848 } 1849 1850 /** 1851 * Returns a module finder that finds all modules in the given list 1852 */ finderOf(Collection<ModuleInfo> moduleInfos)1853 private static ModuleFinder finderOf(Collection<ModuleInfo> moduleInfos) { 1854 Supplier<ModuleReader> readerSupplier = () -> null; 1855 Map<String, ModuleReference> namesToReference = new HashMap<>(); 1856 for (ModuleInfo mi : moduleInfos) { 1857 String name = mi.moduleName(); 1858 ModuleReference mref 1859 = new ModuleReferenceImpl(mi.descriptor(), 1860 URI.create("jrt:/" + name), 1861 readerSupplier, 1862 null, 1863 mi.target(), 1864 null, 1865 null, 1866 mi.moduleResolution()); 1867 namesToReference.put(name, mref); 1868 } 1869 1870 return new ModuleFinder() { 1871 @Override 1872 public Optional<ModuleReference> find(String name) { 1873 Objects.requireNonNull(name); 1874 return Optional.ofNullable(namesToReference.get(name)); 1875 } 1876 @Override 1877 public Set<ModuleReference> findAll() { 1878 return new HashSet<>(namesToReference.values()); 1879 } 1880 }; 1881 } 1882 } 1883