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