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