1 /* 2 * Copyright (c) 2015, 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.*; 33 import java.util.ArrayList; 34 import java.util.Collection; 35 import java.util.EnumSet; 36 import java.util.HashMap; 37 import java.util.HashSet; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Objects; 41 import java.util.Set; 42 import java.util.function.IntSupplier; 43 44 import jdk.internal.misc.JavaLangModuleAccess; 45 import jdk.internal.misc.SharedSecrets; 46 import jdk.internal.module.Checks; 47 import jdk.internal.module.ModuleInfoExtender; 48 import jdk.internal.module.SystemModules; 49 import jdk.internal.module.WarnIfResolvedReason; 50 import jdk.internal.org.objectweb.asm.ClassWriter; 51 import jdk.internal.org.objectweb.asm.MethodVisitor; 52 import jdk.internal.org.objectweb.asm.Opcodes; 53 54 import static jdk.internal.org.objectweb.asm.Opcodes.*; 55 56 import jdk.tools.jlink.plugin.PluginException; 57 import jdk.tools.jlink.plugin.ResourcePool; 58 import jdk.tools.jlink.plugin.Plugin; 59 import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.SystemModulesClassGenerator.*; 60 import jdk.tools.jlink.plugin.ResourcePoolBuilder; 61 import jdk.tools.jlink.plugin.ResourcePoolEntry; 62 63 /** 64 * Jlink plugin to reconstitute module descriptors for system modules. 65 * It will extend module-info.class with Packages attribute, 66 * if not present. It also determines the number of packages of 67 * the boot layer at link time. 68 * 69 * This plugin will override jdk.internal.module.SystemModules class 70 * 71 * @see java.lang.module.SystemModuleFinder 72 * @see SystemModules 73 */ 74 public final class SystemModuleDescriptorPlugin implements Plugin { 75 private static final JavaLangModuleAccess JLMA = 76 SharedSecrets.getJavaLangModuleAccess(); 77 78 private static final String NAME = "system-modules"; 79 private static final String DESCRIPTION = 80 PluginsResourceBundle.getDescription(NAME); 81 82 private boolean enabled; 83 public SystemModuleDescriptorPlugin() { 84 this.enabled = true; 85 } 86 87 @Override 88 public String getName() { 89 return NAME; 90 } 91 92 @Override 93 public String getDescription() { 94 return DESCRIPTION; 95 } 96 97 @Override 98 public Set<State> getState() { 99 return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL) 100 : EnumSet.of(State.DISABLED); 101 } 102 103 @Override 104 public void configure(Map<String, String> config) { 105 if (config.containsKey(NAME)) { 106 enabled = false; 107 } 108 } 109 110 @Override 111 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 112 if (!enabled) { 113 throw new PluginException(NAME + " was set"); 114 } 115 116 SystemModulesClassGenerator generator = new SystemModulesClassGenerator(); 117 118 // generate the byte code to create ModuleDescriptors 119 // skip parsing module-info.class and skip name check 120 in.moduleView().modules().forEach(module -> { 121 122 ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow( 123 // automatic module not supported yet 124 () -> new PluginException("module-info.class not found for " + 125 module.name() + " module") 126 ); 127 128 assert module.name().equals(data.moduleName()); 129 try { 130 ByteArrayInputStream bain = new ByteArrayInputStream(data.contentBytes()); 131 ModuleDescriptor md = ModuleDescriptor.read(bain); 132 validateNames(md); 133 134 Set<String> packages = module.packages(); 135 generator.addModule(md, module.packages()); 136 137 // add Packages attribute if not exist 138 if (md.packages().isEmpty() && packages.size() > 0) { 139 bain.reset(); 140 ModuleInfoRewriter minfoWriter = 141 new ModuleInfoRewriter(bain, module.packages()); 142 // replace with the overridden version 143 data = data.copyWithContent(minfoWriter.getBytes()); 144 } 145 out.add(data); 146 } catch (IOException e) { 147 throw new PluginException(e); 148 } 149 }); 150 151 // Generate the new class 152 ClassWriter cwriter = generator.getClassWriter(); 153 in.entries().forEach(data -> { 154 if (data.path().endsWith("module-info.class")) 155 return; 156 if (generator.isOverriddenClass(data.path())) { 157 byte[] bytes = cwriter.toByteArray(); 158 ResourcePoolEntry ndata = data.copyWithContent(bytes); 159 out.add(ndata); 160 } else { 161 out.add(data); 162 } 163 }); 164 165 return out.build(); 166 } 167 168 /* 169 * Add Packages attribute 170 */ 171 class ModuleInfoRewriter extends ByteArrayOutputStream { 172 final ModuleInfoExtender extender; 173 ModuleInfoRewriter(InputStream in, Set<String> packages) throws IOException { 174 this.extender = ModuleInfoExtender.newExtender(in); 175 // Add Packages attribute 176 this.extender.packages(packages); 177 this.extender.write(this); 178 } 179 180 byte[] getBytes() { 181 return buf; 182 } 183 } 184 185 void validateNames(ModuleDescriptor md) { 186 Checks.requireModuleName(md.name()); 187 for (Requires req : md.requires()) { 188 Checks.requireModuleName(req.name()); 189 } 190 for (Exports e : md.exports()) { 191 Checks.requirePackageName(e.source()); 192 if (e.isQualified()) 193 e.targets().forEach(Checks::requireModuleName); 194 } 195 for (Opens opens : md.opens()) { 196 Checks.requirePackageName(opens.source()); 197 if (opens.isQualified()) 198 opens.targets().forEach(Checks::requireModuleName); 199 } 200 for (Provides provides : md.provides()) { 201 Checks.requireServiceTypeName(provides.service()); 202 provides.providers().forEach(Checks::requireServiceProviderName); 203 } 204 for (String service : md.uses()) { 205 Checks.requireServiceTypeName(service); 206 } 207 for (String pn : md.packages()) { 208 Checks.requirePackageName(pn); 209 } 210 } 211 212 /* 213 * Returns the initial capacity for a new Set or Map of the given size 214 * to avoid resizing. 215 */ 216 static final int initialCapacity(int size) { 217 if (size == 0) { 218 return 0; 219 } else { 220 // Adjust to try and get size/capacity as close to the 221 // HashSet/HashMap default load factor without going over. 222 return (int)(Math.ceil((double)size / 0.75)); 223 } 224 } 225 226 /** 227 * ClassWriter of a new jdk.internal.module.SystemModules class 228 * to reconstitute ModuleDescriptor of the system modules. 229 */ 230 static class SystemModulesClassGenerator { 231 private static final String CLASSNAME = 232 "jdk/internal/module/SystemModules"; 233 private static final String MODULE_DESCRIPTOR_BUILDER = 234 "jdk/internal/module/Builder"; 235 private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE = 236 "[Ljava/lang/module/ModuleDescriptor;"; 237 private static final String REQUIRES_MODIFIER_CLASSNAME = 238 "java/lang/module/ModuleDescriptor$Requires$Modifier"; 239 private static final String EXPORTS_MODIFIER_CLASSNAME = 240 "java/lang/module/ModuleDescriptor$Exports$Modifier"; 241 private static final String OPENS_MODIFIER_CLASSNAME = 242 "java/lang/module/ModuleDescriptor$Opens$Modifier"; 243 244 // static variables in SystemModules class 245 private static final String MODULE_NAMES = "MODULE_NAMES"; 246 private static final String MODULES_TO_HASH = "MODULES_TO_HASH"; 247 private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER"; 248 249 private static final int MAX_LOCAL_VARS = 256; 250 251 private final int BUILDER_VAR = 0; 252 private final int MD_VAR = 1; // variable for ModuleDescriptor 253 private int nextLocalVar = 2; // index to next local variable 254 255 private final ClassWriter cw; 256 private MethodVisitor mv; 257 private int nextModulesIndex = 0; 258 259 // list of all ModuleDescriptorBuilders, invoked in turn when building. 260 private final List<ModuleDescriptorBuilder> builders = new ArrayList<>(); 261 262 // module name to hash 263 private final Map<String, byte[]> modulesToHash = new HashMap<>(); 264 265 // module name to index in MODULES_TO_HASH 266 private final Map<String, Integer> modulesToHashIndex = new HashMap<>(); 267 268 // A builder to create one single Set instance for a given set of 269 // names or modifiers to reduce the footprint 270 // e.g. target modules of qualified exports 271 private final DedupSetBuilder dedupSetBuilder 272 = new DedupSetBuilder(this::getNextLocalVar); 273 274 public SystemModulesClassGenerator() { 275 this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + 276 ClassWriter.COMPUTE_FRAMES); 277 } 278 279 private int getNextLocalVar() { 280 return nextLocalVar++; 281 } 282 283 /* 284 * static initializer initializing the static fields 285 * 286 * static Map<String, ModuleDescriptor> map = new HashMap<>(); 287 */ 288 private void clinit(int numModules, int numPackages) { 289 cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME, 290 null, "java/lang/Object", null); 291 292 // public static String[] MODULE_NAMES = new String[] {....}; 293 cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES, 294 "[Ljava/lang/String;", null, null) 295 .visitEnd(); 296 297 // public static byte[][] MODULES_TO_HASH 298 cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULES_TO_HASH, 299 "[[B", null, null) 300 .visitEnd(); 301 302 // public static int PACKAGES_IN_BOOT_LAYER; 303 cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT, 304 "I", null, numPackages) 305 .visitEnd(); 306 307 this.mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", 308 null, null); 309 mv.visitCode(); 310 311 // create the MODULE_NAMES array 312 pushInt(numModules); 313 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 314 315 int index = 0; 316 for (ModuleDescriptorBuilder builder : builders) { 317 mv.visitInsn(DUP); // arrayref 318 pushInt(index++); 319 mv.visitLdcInsn(builder.md.name()); // value 320 mv.visitInsn(AASTORE); 321 } 322 323 mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES, 324 "[Ljava/lang/String;"); 325 326 // create the MODULES_TO_HASH array 327 pushInt(numModules); 328 mv.visitTypeInsn(ANEWARRAY, "[B"); 329 330 index = 0; 331 for (ModuleDescriptorBuilder builder : builders) { 332 String mn = builder.md.name(); 333 byte[] recordedHash = modulesToHash.get(mn); 334 if (recordedHash != null) { 335 mv.visitInsn(DUP); // arrayref 336 pushInt(index); 337 pushInt(recordedHash.length); 338 mv.visitIntInsn(NEWARRAY, T_BYTE); 339 for (int i = 0; i < recordedHash.length; i++) { 340 mv.visitInsn(DUP); // arrayref 341 pushInt(i); 342 mv.visitIntInsn(BIPUSH, recordedHash[i]); 343 mv.visitInsn(BASTORE); 344 } 345 mv.visitInsn(AASTORE); 346 modulesToHashIndex.put(mn, index); 347 } 348 index++; 349 } 350 351 mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULES_TO_HASH, "[[B"); 352 353 mv.visitInsn(RETURN); 354 mv.visitMaxs(0, 0); 355 mv.visitEnd(); 356 357 } 358 359 /* 360 * Adds the given ModuleDescriptor to the system module list, and 361 * prepares mapping from various Sets to SetBuilders to emit an 362 * optimized number of sets during build. 363 */ 364 public void addModule(ModuleDescriptor md, Set<String> packages) { 365 ModuleDescriptorBuilder builder = new ModuleDescriptorBuilder(md, packages); 366 builders.add(builder); 367 368 // exports 369 for (Exports e : md.exports()) { 370 dedupSetBuilder.stringSet(e.targets()); 371 dedupSetBuilder.exportsModifiers(e.modifiers()); 372 } 373 374 // opens 375 for (Opens opens : md.opens()) { 376 dedupSetBuilder.stringSet(opens.targets()); 377 dedupSetBuilder.opensModifiers(opens.modifiers()); 378 } 379 380 // requires 381 for (Requires r : md.requires()) { 382 dedupSetBuilder.requiresModifiers(r.modifiers()); 383 } 384 385 // uses 386 dedupSetBuilder.stringSet(md.uses()); 387 388 // hashes 389 JLMA.hashes(md).ifPresent(mh -> modulesToHash.putAll(mh.hashes())); 390 } 391 392 /* 393 * Generate bytecode for SystemModules 394 */ 395 public ClassWriter getClassWriter() { 396 int numModules = builders.size(); 397 int numPackages = 0; 398 for (ModuleDescriptorBuilder builder : builders) { 399 numPackages += builder.md.packages().size(); 400 } 401 402 this.clinit(numModules, numPackages); 403 this.mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC, 404 "modules", "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, 405 "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, null); 406 mv.visitCode(); 407 pushInt(numModules); 408 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor"); 409 mv.visitVarInsn(ASTORE, MD_VAR); 410 411 for (ModuleDescriptorBuilder builder : builders) { 412 builder.build(); 413 } 414 mv.visitVarInsn(ALOAD, MD_VAR); 415 mv.visitInsn(ARETURN); 416 mv.visitMaxs(0, 0); 417 mv.visitEnd(); 418 return cw; 419 } 420 421 public boolean isOverriddenClass(String path) { 422 return path.equals("/java.base/" + CLASSNAME + ".class"); 423 } 424 425 void pushInt(int num) { 426 if (num <= 5) { 427 mv.visitInsn(ICONST_0 + num); 428 } else if (num < Byte.MAX_VALUE) { 429 mv.visitIntInsn(BIPUSH, num); 430 } else if (num < Short.MAX_VALUE) { 431 mv.visitIntInsn(SIPUSH, num); 432 } else { 433 throw new IllegalArgumentException("exceed limit: " + num); 434 } 435 } 436 437 class ModuleDescriptorBuilder { 438 static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;"; 439 static final String EXPORTS_TYPE = 440 "Ljava/lang/module/ModuleDescriptor$Exports;"; 441 static final String OPENS_TYPE = 442 "Ljava/lang/module/ModuleDescriptor$Opens;"; 443 static final String PROVIDES_TYPE = 444 "Ljava/lang/module/ModuleDescriptor$Provides;"; 445 static final String REQUIRES_TYPE = 446 "Ljava/lang/module/ModuleDescriptor$Requires;"; 447 448 // method signature for static Builder::newExports, newOpens, 449 // newProvides, newRequires methods 450 static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG = 451 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)" 452 + EXPORTS_TYPE; 453 static final String EXPORTS_MODIFIER_SET_STRING_SIG = 454 "(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE; 455 static final String OPENS_MODIFIER_SET_STRING_SET_SIG = 456 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)" 457 + OPENS_TYPE; 458 static final String OPENS_MODIFIER_SET_STRING_SIG = 459 "(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE; 460 static final String PROVIDES_STRING_LIST_SIG = 461 "(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE; 462 static final String REQUIRES_SET_STRING_SIG = 463 "(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE; 464 465 // method signature for Builder instance methods that 466 // return this Builder instance 467 static final String EXPORTS_ARRAY_SIG = 468 "([" + EXPORTS_TYPE + ")" + BUILDER_TYPE; 469 static final String OPENS_ARRAY_SIG = 470 "([" + OPENS_TYPE + ")" + BUILDER_TYPE; 471 static final String PROVIDES_ARRAY_SIG = 472 "([" + PROVIDES_TYPE + ")" + BUILDER_TYPE; 473 static final String REQUIRES_ARRAY_SIG = 474 "([" + REQUIRES_TYPE + ")" + BUILDER_TYPE; 475 static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE; 476 static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE; 477 static final String STRING_BYTE_ARRAY_SIG = 478 "(Ljava/lang/String;[B)" + BUILDER_TYPE; 479 static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE; 480 481 482 final ModuleDescriptor md; 483 final Set<String> packages; 484 485 ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages) { 486 if (md.isAutomatic()) { 487 throw new InternalError("linking automatic module is not supported"); 488 } 489 this.md = md; 490 this.packages = packages; 491 } 492 493 void build() { 494 // new jdk.internal.module.Builder 495 newBuilder(); 496 497 // requires 498 requires(md.requires()); 499 500 // exports 501 exports(md.exports()); 502 503 // opens 504 opens(md.opens()); 505 506 // uses 507 uses(md.uses()); 508 509 // provides 510 provides(md.provides()); 511 512 // all packages 513 packages(packages); 514 515 // version 516 md.version().ifPresent(this::version); 517 518 // main class 519 md.mainClass().ifPresent(this::mainClass); 520 521 // hashes 522 JLMA.hashes(md).ifPresent(mh -> { 523 algorithm(mh.algorithm()); 524 mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn))); 525 }); 526 527 // DO_NOT_RESOLVE_BY_DEFAULT 528 if (JLMA.doNotResolveByDefault(md)) { 529 setModuleBit("doNotResolveByDefault", true); 530 } 531 532 // WARN_IF_RESOLVED reason 533 if (JLMA.warnIfResolvedReason(md) != WarnIfResolvedReason.NONE) 534 warnIfResolved(JLMA.warnIfResolvedReason(md)); 535 putModuleDescriptor(); 536 } 537 538 void newBuilder() { 539 mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER); 540 mv.visitInsn(DUP); 541 mv.visitLdcInsn(md.name()); 542 mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER, 543 "<init>", "(Ljava/lang/String;)V", false); 544 mv.visitVarInsn(ASTORE, BUILDER_VAR); 545 mv.visitVarInsn(ALOAD, BUILDER_VAR); 546 547 if (md.isOpen()) { 548 setModuleBit("open", true); 549 } 550 if (md.isSynthetic()) { 551 setModuleBit("synthetic", true); 552 } 553 } 554 555 /* 556 * Invoke Builder.<methodName>(boolean value) 557 */ 558 void setModuleBit(String methodName, boolean value) { 559 mv.visitVarInsn(ALOAD, BUILDER_VAR); 560 if (value) { 561 mv.visitInsn(ICONST_1); 562 } else { 563 mv.visitInsn(ICONST_0); 564 } 565 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 566 methodName, BOOLEAN_SIG, false); 567 mv.visitInsn(POP); 568 } 569 570 /* 571 * Put ModuleDescriptor into the modules array 572 */ 573 void putModuleDescriptor() { 574 mv.visitVarInsn(ALOAD, MD_VAR); 575 pushInt(nextModulesIndex++); 576 mv.visitVarInsn(ALOAD, BUILDER_VAR); 577 mv.visitLdcInsn(md.hashCode()); 578 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 579 "build", "(I)Ljava/lang/module/ModuleDescriptor;", 580 false); 581 mv.visitInsn(AASTORE); 582 } 583 584 /* 585 * Call Builder::newRequires to create Requires instances and 586 * then pass it to the builder by calling: 587 * Builder.requires(Requires[]) 588 * 589 */ 590 void requires(Set<Requires> requires) { 591 mv.visitVarInsn(ALOAD, BUILDER_VAR); 592 pushInt(requires.size()); 593 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires"); 594 int arrayIndex = 0; 595 for (Requires require : requires) { 596 mv.visitInsn(DUP); // arrayref 597 pushInt(arrayIndex++); 598 newRequires(require.modifiers(), require.name()); 599 mv.visitInsn(AASTORE); 600 } 601 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 602 "requires", REQUIRES_ARRAY_SIG, false); 603 } 604 605 /* 606 * Invoke Builder.newRequires(Set<Modifier> mods, String mn) 607 * 608 * Set<Modifier> mods = ... 609 * Builder.newRequires(mods, mn); 610 */ 611 void newRequires(Set<Requires.Modifier> mods, String name) { 612 int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods); 613 mv.visitVarInsn(ALOAD, varIndex); 614 mv.visitLdcInsn(name); 615 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 616 "newRequires", REQUIRES_SET_STRING_SIG, false); 617 } 618 619 /* 620 * Call Builder::newExports to create Exports instances and 621 * then pass it to the builder by calling: 622 * Builder.exports(Exports[]) 623 * 624 */ 625 void exports(Set<Exports> exports) { 626 mv.visitVarInsn(ALOAD, BUILDER_VAR); 627 pushInt(exports.size()); 628 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports"); 629 int arrayIndex = 0; 630 for (Exports export : exports) { 631 mv.visitInsn(DUP); // arrayref 632 pushInt(arrayIndex++); 633 newExports(export.modifiers(), export.source(), export.targets()); 634 mv.visitInsn(AASTORE); 635 } 636 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 637 "exports", EXPORTS_ARRAY_SIG, false); 638 } 639 640 /* 641 * Invoke 642 * Builder.newExports(Set<Exports.Modifier> ms, String pn, 643 * Set<String> targets) 644 * or 645 * Builder.newExports(Set<Exports.Modifier> ms, String pn) 646 * 647 * Set<String> targets = new HashSet<>(); 648 * targets.add(t); 649 * : 650 * : 651 * 652 * Set<Modifier> mods = ... 653 * Builder.newExports(mods, pn, targets); 654 */ 655 void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) { 656 int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms); 657 if (!targets.isEmpty()) { 658 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 659 mv.visitVarInsn(ALOAD, modifiersSetIndex); 660 mv.visitLdcInsn(pn); 661 mv.visitVarInsn(ALOAD, stringSetIndex); 662 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 663 "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false); 664 } else { 665 mv.visitVarInsn(ALOAD, modifiersSetIndex); 666 mv.visitLdcInsn(pn); 667 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 668 "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false); 669 } 670 } 671 672 673 /** 674 * Call Builder::newOpens to create Opens instances and 675 * then pass it to the builder by calling: 676 * Builder.opens(Opens[]) 677 * 678 */ 679 void opens(Set<Opens> opens) { 680 mv.visitVarInsn(ALOAD, BUILDER_VAR); 681 pushInt(opens.size()); 682 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens"); 683 int arrayIndex = 0; 684 for (Opens open : opens) { 685 mv.visitInsn(DUP); // arrayref 686 pushInt(arrayIndex++); 687 newOpens(open.modifiers(), open.source(), open.targets()); 688 mv.visitInsn(AASTORE); 689 } 690 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 691 "opens", OPENS_ARRAY_SIG, false); 692 } 693 694 /* 695 * Invoke 696 * Builder.newOpens(Set<Opens.Modifier> ms, String pn, 697 * Set<String> targets) 698 * or 699 * Builder.newOpens(Set<Opens.Modifier> ms, String pn) 700 * 701 * Set<String> targets = new HashSet<>(); 702 * targets.add(t); 703 * : 704 * : 705 * 706 * Set<Modifier> mods = ... 707 * Builder.newOpens(mods, pn, targets); 708 */ 709 void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) { 710 int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms); 711 if (!targets.isEmpty()) { 712 int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets); 713 mv.visitVarInsn(ALOAD, modifiersSetIndex); 714 mv.visitLdcInsn(pn); 715 mv.visitVarInsn(ALOAD, stringSetIndex); 716 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 717 "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false); 718 } else { 719 mv.visitVarInsn(ALOAD, modifiersSetIndex); 720 mv.visitLdcInsn(pn); 721 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 722 "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false); 723 } 724 } 725 726 /* 727 * Invoke Builder.uses(Set<String> uses) 728 */ 729 void uses(Set<String> uses) { 730 int varIndex = dedupSetBuilder.indexOfStringSet(uses); 731 mv.visitVarInsn(ALOAD, BUILDER_VAR); 732 mv.visitVarInsn(ALOAD, varIndex); 733 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 734 "uses", SET_SIG, false); 735 mv.visitInsn(POP); 736 } 737 738 /* 739 * Call Builder::newProvides to create Provides instances and 740 * then pass it to the builder by calling: 741 * Builder.provides(Provides[] provides) 742 * 743 */ 744 void provides(Collection<Provides> provides) { 745 mv.visitVarInsn(ALOAD, BUILDER_VAR); 746 pushInt(provides.size()); 747 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides"); 748 int arrayIndex = 0; 749 for (Provides provide : provides) { 750 mv.visitInsn(DUP); // arrayref 751 pushInt(arrayIndex++); 752 newProvides(provide.service(), provide.providers()); 753 mv.visitInsn(AASTORE); 754 } 755 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 756 "provides", PROVIDES_ARRAY_SIG, false); 757 } 758 759 /* 760 * Invoke Builder.newProvides(String service, Set<String> providers) 761 * 762 * Set<String> providers = new HashSet<>(); 763 * providers.add(impl); 764 * : 765 * : 766 * Builder.newProvides(service, providers); 767 */ 768 void newProvides(String service, List<String> providers) { 769 mv.visitLdcInsn(service); 770 pushInt(providers.size()); 771 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 772 int arrayIndex = 0; 773 for (String provider : providers) { 774 mv.visitInsn(DUP); // arrayref 775 pushInt(arrayIndex++); 776 mv.visitLdcInsn(provider); 777 mv.visitInsn(AASTORE); 778 } 779 mv.visitMethodInsn(INVOKESTATIC, "java/util/List", 780 "of", "([Ljava/lang/Object;)Ljava/util/List;", true); 781 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER, 782 "newProvides", PROVIDES_STRING_LIST_SIG, false); 783 } 784 785 /* 786 * Invoke Builder.packages(String pn) 787 */ 788 void packages(Set<String> packages) { 789 int varIndex = dedupSetBuilder.newStringSet(packages); 790 mv.visitVarInsn(ALOAD, BUILDER_VAR); 791 mv.visitVarInsn(ALOAD, varIndex); 792 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 793 "packages", SET_SIG, false); 794 mv.visitInsn(POP); 795 } 796 797 /* 798 * Invoke Builder.mainClass(String cn) 799 */ 800 void mainClass(String cn) { 801 mv.visitVarInsn(ALOAD, BUILDER_VAR); 802 mv.visitLdcInsn(cn); 803 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 804 "mainClass", STRING_SIG, false); 805 mv.visitInsn(POP); 806 } 807 808 /* 809 * Invoke Builder.version(Version v); 810 */ 811 void version(Version v) { 812 mv.visitVarInsn(ALOAD, BUILDER_VAR); 813 mv.visitLdcInsn(v.toString()); 814 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 815 "version", STRING_SIG, false); 816 mv.visitInsn(POP); 817 } 818 819 /* 820 * Invoke Builder.algorithm(String a); 821 */ 822 void algorithm(String alg) { 823 mv.visitVarInsn(ALOAD, BUILDER_VAR); 824 mv.visitLdcInsn(alg); 825 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 826 "algorithm", STRING_SIG, false); 827 mv.visitInsn(POP); 828 } 829 830 /* 831 * Invoke Builder.moduleHash(String name, byte[] hash); 832 */ 833 void moduleHash(String name, byte[] hash) { 834 mv.visitVarInsn(ALOAD, BUILDER_VAR); 835 mv.visitLdcInsn(name); 836 837 // must exist 838 Integer index = modulesToHashIndex.get(name); 839 if (index != null) { 840 mv.visitFieldInsn(GETSTATIC, CLASSNAME, MODULES_TO_HASH, "[[B"); 841 pushInt(index); 842 mv.visitInsn(AALOAD); 843 assert(Objects.equals(hash, modulesToHash.get(name))); 844 } else { 845 pushInt(hash.length); 846 mv.visitIntInsn(NEWARRAY, T_BYTE); 847 for (int i = 0; i < hash.length; i++) { 848 mv.visitInsn(DUP); // arrayref 849 pushInt(i); 850 mv.visitIntInsn(BIPUSH, hash[i]); 851 mv.visitInsn(BASTORE); 852 } 853 } 854 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 855 "moduleHash", STRING_BYTE_ARRAY_SIG, false); 856 mv.visitInsn(POP); 857 } 858 859 static final String WARN_IF_RESOLVED_REASON_CLASSNAME = 860 "jdk/internal/module/WarnIfResolvedReason"; 861 static final String WARN_IF_RESOLVED_REASON_TYPE = 862 "Ljdk/internal/module/WarnIfResolvedReason;"; 863 static final String WARN_METHOD_SIG = 864 "(" + WARN_IF_RESOLVED_REASON_TYPE + ")" + BUILDER_TYPE; 865 866 /* 867 * Invoke Builder.warnIfResolved(WarnIfResolvedReason reason) 868 */ 869 void warnIfResolved(WarnIfResolvedReason reason) { 870 mv.visitVarInsn(ALOAD, BUILDER_VAR); 871 mv.visitFieldInsn(GETSTATIC, 872 WARN_IF_RESOLVED_REASON_CLASSNAME, 873 reason.name(), 874 WARN_IF_RESOLVED_REASON_TYPE); 875 mv.visitMethodInsn(INVOKEVIRTUAL, 876 MODULE_DESCRIPTOR_BUILDER, 877 "warnIfResolved", 878 WARN_METHOD_SIG, 879 false); 880 mv.visitInsn(POP); 881 } 882 } 883 884 /* 885 * Wraps set creation, ensuring identical sets are properly deduplicated. 886 */ 887 class DedupSetBuilder { 888 // map Set<String> to a specialized builder to allow them to be 889 // deduplicated as they are requested 890 final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>(); 891 892 // map Set<Requires.Modifier> to a specialized builder to allow them to be 893 // deduplicated as they are requested 894 final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>> 895 requiresModifiersSets = new HashMap<>(); 896 897 // map Set<Exports.Modifier> to a specialized builder to allow them to be 898 // deduplicated as they are requested 899 final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>> 900 exportsModifiersSets = new HashMap<>(); 901 902 // map Set<Opens.Modifier> to a specialized builder to allow them to be 903 // deduplicated as they are requested 904 final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>> 905 opensModifiersSets = new HashMap<>(); 906 907 private final int stringSetVar; 908 private final int enumSetVar; 909 private final IntSupplier localVarSupplier; 910 911 DedupSetBuilder(IntSupplier localVarSupplier) { 912 this.stringSetVar = localVarSupplier.getAsInt(); 913 this.enumSetVar = localVarSupplier.getAsInt(); 914 this.localVarSupplier = localVarSupplier; 915 } 916 917 /* 918 * Add the given set of strings to this builder. 919 */ 920 void stringSet(Set<String> strings) { 921 stringSets.computeIfAbsent(strings, 922 s -> new SetBuilder<>(s, stringSetVar, localVarSupplier) 923 ).increment(); 924 } 925 926 /* 927 * Add the given set of Exports.Modifiers 928 */ 929 void exportsModifiers(Set<Exports.Modifier> mods) { 930 exportsModifiersSets.computeIfAbsent(mods, s -> 931 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME, 932 enumSetVar, localVarSupplier) 933 ).increment(); 934 } 935 936 /* 937 * Add the given set of Opens.Modifiers 938 */ 939 void opensModifiers(Set<Opens.Modifier> mods) { 940 opensModifiersSets.computeIfAbsent(mods, s -> 941 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME, 942 enumSetVar, localVarSupplier) 943 ).increment(); 944 } 945 946 /* 947 * Add the given set of Requires.Modifiers 948 */ 949 void requiresModifiers(Set<Requires.Modifier> mods) { 950 requiresModifiersSets.computeIfAbsent(mods, s -> 951 new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME, 952 enumSetVar, localVarSupplier) 953 ).increment(); 954 } 955 956 /* 957 * Retrieve the index to the given set of Strings. Emit code to 958 * generate it when SetBuilder::build is called. 959 */ 960 int indexOfStringSet(Set<String> names) { 961 return stringSets.get(names).build(); 962 } 963 964 /* 965 * Retrieve the index to the given set of Exports.Modifier. 966 * Emit code to generate it when EnumSetBuilder::build is called. 967 */ 968 int indexOfExportsModifiers(Set<Exports.Modifier> mods) { 969 return exportsModifiersSets.get(mods).build(); 970 } 971 972 /** 973 * Retrieve the index to the given set of Opens.Modifier. 974 * Emit code to generate it when EnumSetBuilder::build is called. 975 */ 976 int indexOfOpensModifiers(Set<Opens.Modifier> mods) { 977 return opensModifiersSets.get(mods).build(); 978 } 979 980 981 /* 982 * Retrieve the index to the given set of Requires.Modifier. 983 * Emit code to generate it when EnumSetBuilder::build is called. 984 */ 985 int indexOfRequiresModifiers(Set<Requires.Modifier> mods) { 986 return requiresModifiersSets.get(mods).build(); 987 } 988 989 /* 990 * Build a new string set without any attempt to deduplicate it. 991 */ 992 int newStringSet(Set<String> names) { 993 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build(); 994 assert index == stringSetVar; 995 return index; 996 } 997 } 998 999 /* 1000 * SetBuilder generates bytecode to create one single instance of Set 1001 * for a given set of elements and assign to a local variable slot. 1002 * When there is only one single reference to a Set<T>, 1003 * it will reuse defaultVarIndex. For a Set with multiple references, 1004 * it will use a new local variable retrieved from the nextLocalVar 1005 */ 1006 class SetBuilder<T> { 1007 private final Set<T> elements; 1008 private final int defaultVarIndex; 1009 private final IntSupplier nextLocalVar; 1010 private int refCount; 1011 private int localVarIndex; 1012 1013 SetBuilder(Set<T> elements, 1014 int defaultVarIndex, 1015 IntSupplier nextLocalVar) { 1016 this.elements = elements; 1017 this.defaultVarIndex = defaultVarIndex; 1018 this.nextLocalVar = nextLocalVar; 1019 } 1020 1021 /* 1022 * Increments the number of references to this particular set. 1023 */ 1024 final void increment() { 1025 refCount++; 1026 } 1027 1028 /** 1029 * Generate the appropriate instructions to load an object reference 1030 * to the element onto the stack. 1031 */ 1032 void visitElement(T element, MethodVisitor mv) { 1033 mv.visitLdcInsn(element); 1034 } 1035 1036 /* 1037 * Build bytecode for the Set represented by this builder, 1038 * or get the local variable index of a previously generated set 1039 * (in the local scope). 1040 * 1041 * @return local variable index of the generated set. 1042 */ 1043 final int build() { 1044 int index = localVarIndex; 1045 if (localVarIndex == 0) { 1046 // if non-empty and more than one set reference this builder, 1047 // emit to a unique local 1048 index = refCount <= 1 ? defaultVarIndex 1049 : nextLocalVar.getAsInt(); 1050 if (index < MAX_LOCAL_VARS) { 1051 localVarIndex = index; 1052 } else { 1053 // overflow: disable optimization by using localVarIndex = 0 1054 index = defaultVarIndex; 1055 } 1056 1057 generateSetOf(index); 1058 } 1059 return index; 1060 } 1061 1062 private void generateSetOf(int index) { 1063 if (elements.size() <= 10) { 1064 // call Set.of(e1, e2, ...) 1065 StringBuilder sb = new StringBuilder("("); 1066 for (T t : elements) { 1067 sb.append("Ljava/lang/Object;"); 1068 visitElement(t, mv); 1069 } 1070 sb.append(")Ljava/util/Set;"); 1071 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1072 "of", sb.toString(), true); 1073 } else { 1074 // call Set.of(E... elements) 1075 pushInt(elements.size()); 1076 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1077 int arrayIndex = 0; 1078 for (T t : elements) { 1079 mv.visitInsn(DUP); // arrayref 1080 pushInt(arrayIndex); 1081 visitElement(t, mv); // value 1082 mv.visitInsn(AASTORE); 1083 arrayIndex++; 1084 } 1085 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1086 "of", "([Ljava/lang/Object;)Ljava/util/Set;", true); 1087 } 1088 mv.visitVarInsn(ASTORE, index); 1089 } 1090 } 1091 1092 /* 1093 * Generates bytecode to create one single instance of EnumSet 1094 * for a given set of modifiers and assign to a local variable slot. 1095 */ 1096 class EnumSetBuilder<T> extends SetBuilder<T> { 1097 1098 private final String className; 1099 1100 EnumSetBuilder(Set<T> modifiers, String className, 1101 int defaultVarIndex, 1102 IntSupplier nextLocalVar) { 1103 super(modifiers, defaultVarIndex, nextLocalVar); 1104 this.className = className; 1105 } 1106 1107 /** 1108 * Loads an Enum field. 1109 */ 1110 void visitElement(T t, MethodVisitor mv) { 1111 mv.visitFieldInsn(GETSTATIC, className, t.toString(), 1112 "L" + className + ";"); 1113 } 1114 } 1115 } 1116 }