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