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 if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) { 778 setModuleBit("mandated", true); 779 } 780 } 781 782 /* 783 * Invoke Builder.<methodName>(boolean value) 784 */ 785 void setModuleBit(String methodName, boolean value) { 786 mv.visitVarInsn(ALOAD, BUILDER_VAR); 787 if (value) { 788 mv.visitInsn(ICONST_1); 789 } else { 790 mv.visitInsn(ICONST_0); 791 } 792 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 793 methodName, BOOLEAN_SIG, false); 794 mv.visitInsn(POP); 795 } 796 797 /* 798 * Put ModuleDescriptor into the modules array 799 */ 800 void putModuleDescriptor() { 801 mv.visitVarInsn(ALOAD, MD_VAR); 802 pushInt(mv, index); 803 mv.visitVarInsn(ALOAD, BUILDER_VAR); 804 mv.visitLdcInsn(md.hashCode()); 805 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 806 "build", "(I)Ljava/lang/module/ModuleDescriptor;", 807 false); 808 mv.visitInsn(AASTORE); 809 } 810 811 /* 812 * Call Builder::newRequires to create Requires instances and 813 * then pass it to the builder by calling: 814 * Builder.requires(Requires[]) 815 * 816 */ 817 void requires(Set<Requires> requires) { 818 mv.visitVarInsn(ALOAD, BUILDER_VAR); 819 pushInt(mv, requires.size()); 820 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires"); 821 int arrayIndex = 0; 822 for (Requires require : requires) { 823 String compiledVersion = null; 824 if (require.compiledVersion().isPresent()) { 825 compiledVersion = require.compiledVersion().get().toString(); 826 } 827 828 mv.visitInsn(DUP); // arrayref 829 pushInt(mv, arrayIndex++); 830 newRequires(require.modifiers(), require.name(), compiledVersion); 831 mv.visitInsn(AASTORE); 832 } 833 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 834 "requires", REQUIRES_ARRAY_SIG, false); 835 } 836 837 /* 838 * Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion) 839 * 840 * Set<Modifier> mods = ... 841 * Builder.newRequires(mods, mn, compiledVersion); 842 */ 843 void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) { 844 int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods); 845 mv.visitVarInsn(ALOAD, varIndex); 846 mv.visitLdcInsn(name); 847 if (compiledVersion != null) { 848 mv.visitLdcInsn(compiledVersion); 849 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 850 "newRequires", REQUIRES_SET_STRING_STRING_SIG, false); 851 } else { 852 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 853 "newRequires", REQUIRES_SET_STRING_SIG, false); 854 } 855 } 856 857 /* 858 * Call Builder::newExports to create Exports instances and 859 * then pass it to the builder by calling: 860 * Builder.exports(Exports[]) 861 * 862 */ 863 void exports(Set<Exports> exports) { 864 mv.visitVarInsn(ALOAD, BUILDER_VAR); 865 pushInt(mv, exports.size()); 866 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports"); 867 int arrayIndex = 0; 868 for (Exports export : exports) { 869 mv.visitInsn(DUP); // arrayref 870 pushInt(mv, arrayIndex++); 871 newExports(export.modifiers(), export.source(), export.targets()); 872 mv.visitInsn(AASTORE); 873 } 874 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 875 "exports", EXPORTS_ARRAY_SIG, false); 876 } 877 878 /* 879 * Invoke 880 * Builder.newExports(Set<Exports.Modifier> ms, String pn, 881 * Set<String> targets) 882 * or 883 * Builder.newExports(Set<Exports.Modifier> ms, String pn) 884 * 885 * Set<String> targets = new HashSet<>(); 886 * targets.add(t); 887 * : 888 * : 889 * 890 * Set<Modifier> mods = ... 891 * Builder.newExports(mods, pn, targets); 892 */ 893 void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) { 894 int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms); 895 if (!targets.isEmpty()) { 896 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 897 mv.visitVarInsn(ALOAD, modifiersSetIndex); 898 mv.visitLdcInsn(pn); 899 mv.visitVarInsn(ALOAD, stringSetIndex); 900 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 901 "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false); 902 } else { 903 mv.visitVarInsn(ALOAD, modifiersSetIndex); 904 mv.visitLdcInsn(pn); 905 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 906 "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false); 907 } 908 } 909 910 911 /** 912 * Call Builder::newOpens to create Opens instances and 913 * then pass it to the builder by calling: 914 * Builder.opens(Opens[]) 915 */ 916 void opens(Set<Opens> opens) { 917 mv.visitVarInsn(ALOAD, BUILDER_VAR); 918 pushInt(mv, opens.size()); 919 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens"); 920 int arrayIndex = 0; 921 for (Opens open : opens) { 922 mv.visitInsn(DUP); // arrayref 923 pushInt(mv, arrayIndex++); 924 newOpens(open.modifiers(), open.source(), open.targets()); 925 mv.visitInsn(AASTORE); 926 } 927 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 928 "opens", OPENS_ARRAY_SIG, false); 929 } 930 931 /* 932 * Invoke 933 * Builder.newOpens(Set<Opens.Modifier> ms, String pn, 934 * Set<String> targets) 935 * or 936 * Builder.newOpens(Set<Opens.Modifier> ms, String pn) 937 * 938 * Set<String> targets = new HashSet<>(); 939 * targets.add(t); 940 * : 941 * : 942 * 943 * Set<Modifier> mods = ... 944 * Builder.newOpens(mods, pn, targets); 945 */ 946 void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) { 947 int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms); 948 if (!targets.isEmpty()) { 949 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 950 mv.visitVarInsn(ALOAD, modifiersSetIndex); 951 mv.visitLdcInsn(pn); 952 mv.visitVarInsn(ALOAD, stringSetIndex); 953 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 954 "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false); 955 } else { 956 mv.visitVarInsn(ALOAD, modifiersSetIndex); 957 mv.visitLdcInsn(pn); 958 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 959 "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false); 960 } 961 } 962 963 /* 964 * Invoke Builder.uses(Set<String> uses) 965 */ 966 void uses(Set<String> uses) { 967 int varIndex = dedupSetBuilder.indexOfStringSet(uses); 968 mv.visitVarInsn(ALOAD, BUILDER_VAR); 969 mv.visitVarInsn(ALOAD, varIndex); 970 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 971 "uses", SET_SIG, false); 972 mv.visitInsn(POP); 973 } 974 975 /* 976 * Call Builder::newProvides to create Provides instances and 977 * then pass it to the builder by calling: 978 * Builder.provides(Provides[] provides) 979 * 980 */ 981 void provides(Collection<Provides> provides) { 982 mv.visitVarInsn(ALOAD, BUILDER_VAR); 983 pushInt(mv, provides.size()); 984 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides"); 985 int arrayIndex = 0; 986 for (Provides provide : provides) { 987 mv.visitInsn(DUP); // arrayref 988 pushInt(mv, arrayIndex++); 989 newProvides(provide.service(), provide.providers()); 990 mv.visitInsn(AASTORE); 991 } 992 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 993 "provides", PROVIDES_ARRAY_SIG, false); 994 } 995 996 /* 997 * Invoke Builder.newProvides(String service, Set<String> providers) 998 * 999 * Set<String> providers = new HashSet<>(); 1000 * providers.add(impl); 1001 * : 1002 * : 1003 * Builder.newProvides(service, providers); 1004 */ 1005 void newProvides(String service, List<String> providers) { 1006 mv.visitLdcInsn(service); 1007 pushInt(mv, providers.size()); 1008 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1009 int arrayIndex = 0; 1010 for (String provider : providers) { 1011 mv.visitInsn(DUP); // arrayref 1012 pushInt(mv, arrayIndex++); 1013 mv.visitLdcInsn(provider); 1014 mv.visitInsn(AASTORE); 1015 } 1016 mv.visitMethodInsn(INVOKESTATIC, "java/util/List", 1017 "of", "([Ljava/lang/Object;)Ljava/util/List;", true); 1018 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 1019 "newProvides", PROVIDES_STRING_LIST_SIG, false); 1020 } 1021 1022 /* 1023 * Invoke Builder.packages(String pn) 1024 */ 1025 void packages(Set<String> packages) { 1026 int varIndex = dedupSetBuilder.newStringSet(packages); 1027 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1028 mv.visitVarInsn(ALOAD, varIndex); 1029 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1030 "packages", SET_SIG, false); 1031 mv.visitInsn(POP); 1032 } 1033 1034 /* 1035 * Invoke Builder.mainClass(String cn) 1036 */ 1037 void mainClass(String cn) { 1038 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1039 mv.visitLdcInsn(cn); 1040 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1041 "mainClass", STRING_SIG, false); 1042 mv.visitInsn(POP); 1043 } 1044 1045 /* 1046 * Invoke Builder.version(Version v); 1047 */ 1048 void version(Version v) { 1049 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1050 mv.visitLdcInsn(v.toString()); 1051 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1052 "version", STRING_SIG, false); 1053 mv.visitInsn(POP); 1054 } 1055 1056 /* 1057 * Invoke Builder.osName(String name) 1058 * Builder.osArch(String arch) 1059 * Builder.osVersion(String version) 1060 */ 1061 void targetPlatform(String osName, String osArch, String osVersion) { 1062 if (osName != null) { 1063 invokeBuilderMethod("osName", osName); 1064 } 1065 1066 if (osArch != null) { 1067 invokeBuilderMethod("osArch", osArch); 1068 } 1069 1070 if (osVersion != null) { 1071 invokeBuilderMethod("osVersion", osVersion); 1072 } 1073 } 1074 1075 void invokeBuilderMethod(String methodName, String value) { 1076 mv.visitVarInsn(ALOAD, BUILDER_VAR); 1077 mv.visitLdcInsn(value); 1078 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 1079 methodName, STRING_SIG, false); 1080 mv.visitInsn(POP); 1081 } 1082 } 1083 1084 class ModuleHashesBuilder { 1085 private static final String MODULE_HASHES_BUILDER = 1086 "jdk/internal/module/ModuleHashes$Builder"; 1087 private static final String MODULE_HASHES_BUILDER_TYPE = 1088 "L" + MODULE_HASHES_BUILDER + ";"; 1089 static final String STRING_BYTE_ARRAY_SIG = 1090 "(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE; 1091 1092 final ModuleHashes recordedHashes; 1093 final MethodVisitor hmv; 1094 final int index; 1095 1096 ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) { 1097 this.recordedHashes = hashes; 1098 this.hmv = hmv; 1099 this.index = index; 1100 } 1101 1102 /** 1103 * Build ModuleHashes 1104 */ 1105 void build() { 1106 if (recordedHashes == null) 1107 return; 1108 1109 // new jdk.internal.module.ModuleHashes.Builder 1110 newModuleHashesBuilder(); 1111 1112 // Invoke ModuleHashes.Builder::hashForModule 1113 recordedHashes 1114 .names() 1115 .forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn))); 1116 1117 // Put ModuleHashes into the hashes array 1118 pushModuleHashes(); 1119 } 1120 1121 1122 /* 1123 * Create ModuleHashes.Builder instance 1124 */ 1125 void newModuleHashesBuilder() { 1126 hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER); 1127 hmv.visitInsn(DUP); 1128 hmv.visitLdcInsn(recordedHashes.algorithm()); 1129 pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1); 1130 hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER, 1131 "<init>", "(Ljava/lang/String;I)V", false); 1132 hmv.visitVarInsn(ASTORE, BUILDER_VAR); 1133 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1134 } 1135 1136 1137 /* 1138 * Invoke ModuleHashes.Builder::build and put the returned 1139 * ModuleHashes to the hashes array 1140 */ 1141 void pushModuleHashes() { 1142 hmv.visitVarInsn(ALOAD, MH_VAR); 1143 pushInt(hmv, index); 1144 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1145 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 1146 "build", "()Ljdk/internal/module/ModuleHashes;", 1147 false); 1148 hmv.visitInsn(AASTORE); 1149 } 1150 1151 /* 1152 * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash); 1153 */ 1154 void hashForModule(String name, byte[] hash) { 1155 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 1156 hmv.visitLdcInsn(name); 1157 1158 pushInt(hmv, hash.length); 1159 hmv.visitIntInsn(NEWARRAY, T_BYTE); 1160 for (int i = 0; i < hash.length; i++) { 1161 hmv.visitInsn(DUP); // arrayref 1162 pushInt(hmv, i); 1163 hmv.visitIntInsn(BIPUSH, hash[i]); 1164 hmv.visitInsn(BASTORE); 1165 } 1166 1167 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 1168 "hashForModule", STRING_BYTE_ARRAY_SIG, false); 1169 hmv.visitInsn(POP); 1170 } 1171 } 1172 1173 /* 1174 * Wraps set creation, ensuring identical sets are properly deduplicated. 1175 */ 1176 class DedupSetBuilder { 1177 // map Set<String> to a specialized builder to allow them to be 1178 // deduplicated as they are requested 1179 final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>(); 1180 1181 // map Set<Requires.Modifier> to a specialized builder to allow them to be 1182 // deduplicated as they are requested 1183 final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>> 1184 requiresModifiersSets = new HashMap<>(); 1185 1186 // map Set<Exports.Modifier> to a specialized builder to allow them to be 1187 // deduplicated as they are requested 1188 final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>> 1189 exportsModifiersSets = new HashMap<>(); 1190 1191 // map Set<Opens.Modifier> to a specialized builder to allow them to be 1192 // deduplicated as they are requested 1193 final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>> 1194 opensModifiersSets = new HashMap<>(); 1195 1196 private final int stringSetVar; 1197 private final int enumSetVar; 1198 private final IntSupplier localVarSupplier; 1199 1200 DedupSetBuilder(IntSupplier localVarSupplier) { 1201 this.stringSetVar = localVarSupplier.getAsInt(); 1202 this.enumSetVar = localVarSupplier.getAsInt(); 1203 this.localVarSupplier = localVarSupplier; 1204 } 1205 1206 /* 1207 * Add the given set of strings to this builder. 1208 */ 1209 void stringSet(Set<String> strings) { 1210 stringSets.computeIfAbsent(strings, 1211 s -> new SetBuilder<>(s, stringSetVar, localVarSupplier) 1212 ).increment(); 1213 } 1214 1215 /* 1216 * Add the given set of Exports.Modifiers 1217 */ 1218 void exportsModifiers(Set<Exports.Modifier> mods) { 1219 exportsModifiersSets.computeIfAbsent(mods, s -> 1220 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME, 1221 enumSetVar, localVarSupplier) 1222 ).increment(); 1223 } 1224 1225 /* 1226 * Add the given set of Opens.Modifiers 1227 */ 1228 void opensModifiers(Set<Opens.Modifier> mods) { 1229 opensModifiersSets.computeIfAbsent(mods, s -> 1230 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME, 1231 enumSetVar, localVarSupplier) 1232 ).increment(); 1233 } 1234 1235 /* 1236 * Add the given set of Requires.Modifiers 1237 */ 1238 void requiresModifiers(Set<Requires.Modifier> mods) { 1239 requiresModifiersSets.computeIfAbsent(mods, s -> 1240 new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME, 1241 enumSetVar, localVarSupplier) 1242 ).increment(); 1243 } 1244 1245 /* 1246 * Retrieve the index to the given set of Strings. Emit code to 1247 * generate it when SetBuilder::build is called. 1248 */ 1249 int indexOfStringSet(Set<String> names) { 1250 return stringSets.get(names).build(); 1251 } 1252 1253 /* 1254 * Retrieve the index to the given set of Exports.Modifier. 1255 * Emit code to generate it when EnumSetBuilder::build is called. 1256 */ 1257 int indexOfExportsModifiers(Set<Exports.Modifier> mods) { 1258 return exportsModifiersSets.get(mods).build(); 1259 } 1260 1261 /** 1262 * Retrieve the index to the given set of Opens.Modifier. 1263 * Emit code to generate it when EnumSetBuilder::build is called. 1264 */ 1265 int indexOfOpensModifiers(Set<Opens.Modifier> mods) { 1266 return opensModifiersSets.get(mods).build(); 1267 } 1268 1269 1270 /* 1271 * Retrieve the index to the given set of Requires.Modifier. 1272 * Emit code to generate it when EnumSetBuilder::build is called. 1273 */ 1274 int indexOfRequiresModifiers(Set<Requires.Modifier> mods) { 1275 return requiresModifiersSets.get(mods).build(); 1276 } 1277 1278 /* 1279 * Build a new string set without any attempt to deduplicate it. 1280 */ 1281 int newStringSet(Set<String> names) { 1282 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build(); 1283 assert index == stringSetVar; 1284 return index; 1285 } 1286 } 1287 1288 /* 1289 * SetBuilder generates bytecode to create one single instance of Set 1290 * for a given set of elements and assign to a local variable slot. 1291 * When there is only one single reference to a Set<T>, 1292 * it will reuse defaultVarIndex. For a Set with multiple references, 1293 * it will use a new local variable retrieved from the nextLocalVar 1294 */ 1295 class SetBuilder<T> { 1296 private final Set<T> elements; 1297 private final int defaultVarIndex; 1298 private final IntSupplier nextLocalVar; 1299 private int refCount; 1300 private int localVarIndex; 1301 1302 SetBuilder(Set<T> elements, 1303 int defaultVarIndex, 1304 IntSupplier nextLocalVar) { 1305 this.elements = elements; 1306 this.defaultVarIndex = defaultVarIndex; 1307 this.nextLocalVar = nextLocalVar; 1308 } 1309 1310 /* 1311 * Increments the number of references to this particular set. 1312 */ 1313 final void increment() { 1314 refCount++; 1315 } 1316 1317 /** 1318 * Generate the appropriate instructions to load an object reference 1319 * to the element onto the stack. 1320 */ 1321 void visitElement(T element, MethodVisitor mv) { 1322 mv.visitLdcInsn(element); 1323 } 1324 1325 /* 1326 * Build bytecode for the Set represented by this builder, 1327 * or get the local variable index of a previously generated set 1328 * (in the local scope). 1329 * 1330 * @return local variable index of the generated set. 1331 */ 1332 final int build() { 1333 int index = localVarIndex; 1334 if (localVarIndex == 0) { 1335 // if non-empty and more than one set reference this builder, 1336 // emit to a unique local 1337 index = refCount <= 1 ? defaultVarIndex 1338 : nextLocalVar.getAsInt(); 1339 if (index < MAX_LOCAL_VARS) { 1340 localVarIndex = index; 1341 } else { 1342 // overflow: disable optimization by using localVarIndex = 0 1343 index = defaultVarIndex; 1344 } 1345 1346 generateSetOf(index); 1347 } 1348 return index; 1349 } 1350 1351 private void generateSetOf(int index) { 1352 if (elements.size() <= 10) { 1353 // call Set.of(e1, e2, ...) 1354 StringBuilder sb = new StringBuilder("("); 1355 for (T t : elements) { 1356 sb.append("Ljava/lang/Object;"); 1357 visitElement(t, mv); 1358 } 1359 sb.append(")Ljava/util/Set;"); 1360 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1361 "of", sb.toString(), true); 1362 } else { 1363 // call Set.of(E... elements) 1364 pushInt(mv, elements.size()); 1365 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1366 int arrayIndex = 0; 1367 for (T t : elements) { 1368 mv.visitInsn(DUP); // arrayref 1369 pushInt(mv, arrayIndex); 1370 visitElement(t, mv); // value 1371 mv.visitInsn(AASTORE); 1372 arrayIndex++; 1373 } 1374 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1375 "of", "([Ljava/lang/Object;)Ljava/util/Set;", true); 1376 } 1377 mv.visitVarInsn(ASTORE, index); 1378 } 1379 } 1380 1381 /* 1382 * Generates bytecode to create one single instance of EnumSet 1383 * for a given set of modifiers and assign to a local variable slot. 1384 */ 1385 class EnumSetBuilder<T> extends SetBuilder<T> { 1386 1387 private final String className; 1388 1389 EnumSetBuilder(Set<T> modifiers, String className, 1390 int defaultVarIndex, 1391 IntSupplier nextLocalVar) { 1392 super(modifiers, defaultVarIndex, nextLocalVar); 1393 this.className = className; 1394 } 1395 1396 /** 1397 * Loads an Enum field. 1398 */ 1399 void visitElement(T t, MethodVisitor mv) { 1400 mv.visitFieldInsn(GETSTATIC, className, t.toString(), 1401 "L" + className + ";"); 1402 } 1403 } 1404 } 1405 }