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