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 pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1); 971 hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER, 972 "<init>", "(Ljava/lang/String;I)V", false); 973 hmv.visitVarInsn(ASTORE, BUILDER_VAR); 974 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 975 } 976 977 978 /* 979 * Invoke ModuleHashes.Builder::build and put the returned 980 * ModuleHashes to the hashes array 981 */ 982 void pushModuleHashes() { 983 hmv.visitVarInsn(ALOAD, MH_VAR); 984 pushInt(hmv, index); 985 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 986 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 987 "build", "()Ljdk/internal/module/ModuleHashes;", 988 false); 989 hmv.visitInsn(AASTORE); 990 } 991 992 /* 993 * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash); 994 */ 995 void hashForModule(String name, byte[] hash) { 996 hmv.visitVarInsn(ALOAD, BUILDER_VAR); 997 hmv.visitLdcInsn(name); 998 999 pushInt(hmv, hash.length); 1000 hmv.visitIntInsn(NEWARRAY, T_BYTE); 1001 for (int i = 0; i < hash.length; i++) { 1002 hmv.visitInsn(DUP); // arrayref 1003 pushInt(hmv, i); 1004 hmv.visitIntInsn(BIPUSH, hash[i]); 1005 hmv.visitInsn(BASTORE); 1006 } 1007 1008 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER, 1009 "hashForModule", STRING_BYTE_ARRAY_SIG, false); 1010 hmv.visitInsn(POP); 1011 } 1012 } 1013 1014 /* 1015 * Wraps set creation, ensuring identical sets are properly deduplicated. 1016 */ 1017 class DedupSetBuilder { 1018 // map Set<String> to a specialized builder to allow them to be 1019 // deduplicated as they are requested 1020 final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>(); 1021 1022 // map Set<Requires.Modifier> to a specialized builder to allow them to be 1023 // deduplicated as they are requested 1024 final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>> 1025 requiresModifiersSets = new HashMap<>(); 1026 1027 // map Set<Exports.Modifier> to a specialized builder to allow them to be 1028 // deduplicated as they are requested 1029 final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>> 1030 exportsModifiersSets = new HashMap<>(); 1031 1032 // map Set<Opens.Modifier> to a specialized builder to allow them to be 1033 // deduplicated as they are requested 1034 final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>> 1035 opensModifiersSets = new HashMap<>(); 1036 1037 private final int stringSetVar; 1038 private final int enumSetVar; 1039 private final IntSupplier localVarSupplier; 1040 1041 DedupSetBuilder(IntSupplier localVarSupplier) { 1042 this.stringSetVar = localVarSupplier.getAsInt(); 1043 this.enumSetVar = localVarSupplier.getAsInt(); 1044 this.localVarSupplier = localVarSupplier; 1045 } 1046 1047 /* 1048 * Add the given set of strings to this builder. 1049 */ 1050 void stringSet(Set<String> strings) { 1051 stringSets.computeIfAbsent(strings, 1052 s -> new SetBuilder<>(s, stringSetVar, localVarSupplier) 1053 ).increment(); 1054 } 1055 1056 /* 1057 * Add the given set of Exports.Modifiers 1058 */ 1059 void exportsModifiers(Set<Exports.Modifier> mods) { 1060 exportsModifiersSets.computeIfAbsent(mods, s -> 1061 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME, 1062 enumSetVar, localVarSupplier) 1063 ).increment(); 1064 } 1065 1066 /* 1067 * Add the given set of Opens.Modifiers 1068 */ 1069 void opensModifiers(Set<Opens.Modifier> mods) { 1070 opensModifiersSets.computeIfAbsent(mods, s -> 1071 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME, 1072 enumSetVar, localVarSupplier) 1073 ).increment(); 1074 } 1075 1076 /* 1077 * Add the given set of Requires.Modifiers 1078 */ 1079 void requiresModifiers(Set<Requires.Modifier> mods) { 1080 requiresModifiersSets.computeIfAbsent(mods, s -> 1081 new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME, 1082 enumSetVar, localVarSupplier) 1083 ).increment(); 1084 } 1085 1086 /* 1087 * Retrieve the index to the given set of Strings. Emit code to 1088 * generate it when SetBuilder::build is called. 1089 */ 1090 int indexOfStringSet(Set<String> names) { 1091 return stringSets.get(names).build(); 1092 } 1093 1094 /* 1095 * Retrieve the index to the given set of Exports.Modifier. 1096 * Emit code to generate it when EnumSetBuilder::build is called. 1097 */ 1098 int indexOfExportsModifiers(Set<Exports.Modifier> mods) { 1099 return exportsModifiersSets.get(mods).build(); 1100 } 1101 1102 /** 1103 * Retrieve the index to the given set of Opens.Modifier. 1104 * Emit code to generate it when EnumSetBuilder::build is called. 1105 */ 1106 int indexOfOpensModifiers(Set<Opens.Modifier> mods) { 1107 return opensModifiersSets.get(mods).build(); 1108 } 1109 1110 1111 /* 1112 * Retrieve the index to the given set of Requires.Modifier. 1113 * Emit code to generate it when EnumSetBuilder::build is called. 1114 */ 1115 int indexOfRequiresModifiers(Set<Requires.Modifier> mods) { 1116 return requiresModifiersSets.get(mods).build(); 1117 } 1118 1119 /* 1120 * Build a new string set without any attempt to deduplicate it. 1121 */ 1122 int newStringSet(Set<String> names) { 1123 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build(); 1124 assert index == stringSetVar; 1125 return index; 1126 } 1127 } 1128 1129 /* 1130 * SetBuilder generates bytecode to create one single instance of Set 1131 * for a given set of elements and assign to a local variable slot. 1132 * When there is only one single reference to a Set<T>, 1133 * it will reuse defaultVarIndex. For a Set with multiple references, 1134 * it will use a new local variable retrieved from the nextLocalVar 1135 */ 1136 class SetBuilder<T> { 1137 private final Set<T> elements; 1138 private final int defaultVarIndex; 1139 private final IntSupplier nextLocalVar; 1140 private int refCount; 1141 private int localVarIndex; 1142 1143 SetBuilder(Set<T> elements, 1144 int defaultVarIndex, 1145 IntSupplier nextLocalVar) { 1146 this.elements = elements; 1147 this.defaultVarIndex = defaultVarIndex; 1148 this.nextLocalVar = nextLocalVar; 1149 } 1150 1151 /* 1152 * Increments the number of references to this particular set. 1153 */ 1154 final void increment() { 1155 refCount++; 1156 } 1157 1158 /** 1159 * Generate the appropriate instructions to load an object reference 1160 * to the element onto the stack. 1161 */ 1162 void visitElement(T element, MethodVisitor mv) { 1163 mv.visitLdcInsn(element); 1164 } 1165 1166 /* 1167 * Build bytecode for the Set represented by this builder, 1168 * or get the local variable index of a previously generated set 1169 * (in the local scope). 1170 * 1171 * @return local variable index of the generated set. 1172 */ 1173 final int build() { 1174 int index = localVarIndex; 1175 if (localVarIndex == 0) { 1176 // if non-empty and more than one set reference this builder, 1177 // emit to a unique local 1178 index = refCount <= 1 ? defaultVarIndex 1179 : nextLocalVar.getAsInt(); 1180 if (index < MAX_LOCAL_VARS) { 1181 localVarIndex = index; 1182 } else { 1183 // overflow: disable optimization by using localVarIndex = 0 1184 index = defaultVarIndex; 1185 } 1186 1187 generateSetOf(index); 1188 } 1189 return index; 1190 } 1191 1192 private void generateSetOf(int index) { 1193 if (elements.size() <= 10) { 1194 // call Set.of(e1, e2, ...) 1195 StringBuilder sb = new StringBuilder("("); 1196 for (T t : elements) { 1197 sb.append("Ljava/lang/Object;"); 1198 visitElement(t, mv); 1199 } 1200 sb.append(")Ljava/util/Set;"); 1201 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1202 "of", sb.toString(), true); 1203 } else { 1204 // call Set.of(E... elements) 1205 pushInt(mv, elements.size()); 1206 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 1207 int arrayIndex = 0; 1208 for (T t : elements) { 1209 mv.visitInsn(DUP); // arrayref 1210 pushInt(mv, arrayIndex); 1211 visitElement(t, mv); // value 1212 mv.visitInsn(AASTORE); 1213 arrayIndex++; 1214 } 1215 mv.visitMethodInsn(INVOKESTATIC, "java/util/Set", 1216 "of", "([Ljava/lang/Object;)Ljava/util/Set;", true); 1217 } 1218 mv.visitVarInsn(ASTORE, index); 1219 } 1220 } 1221 1222 /* 1223 * Generates bytecode to create one single instance of EnumSet 1224 * for a given set of modifiers and assign to a local variable slot. 1225 */ 1226 class EnumSetBuilder<T> extends SetBuilder<T> { 1227 1228 private final String className; 1229 1230 EnumSetBuilder(Set<T> modifiers, String className, 1231 int defaultVarIndex, 1232 IntSupplier nextLocalVar) { 1233 super(modifiers, defaultVarIndex, nextLocalVar); 1234 this.className = className; 1235 } 1236 1237 /** 1238 * Loads an Enum field. 1239 */ 1240 void visitElement(T t, MethodVisitor mv) { 1241 mv.visitFieldInsn(GETSTATIC, className, t.toString(), 1242 "L" + className + ";"); 1243 } 1244 } 1245 } 1246 }