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