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