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.Collections; 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.stream.Collectors; 41 42 import jdk.internal.module.Checks; 43 import jdk.internal.module.ModuleInfoExtender; 44 import jdk.internal.module.SystemModules; 45 import jdk.internal.org.objectweb.asm.ClassWriter; 46 import jdk.internal.org.objectweb.asm.MethodVisitor; 47 import jdk.internal.org.objectweb.asm.Opcodes; 48 49 import static jdk.internal.org.objectweb.asm.Opcodes.*; 50 import jdk.tools.jlink.plugin.PluginException; 51 import jdk.tools.jlink.plugin.Pool; 52 import jdk.tools.jlink.plugin.TransformerPlugin; 53 54 /** 55 * Jlink plugin to reconstitute module descriptors for installed modules. 56 * It will extend module-info.class with ConcealedPackages attribute, 57 * if not present. It also determines the number of packages of 58 * the boot layer at link time. 59 * 60 * This plugin will override jdk.internal.module.SystemModules class 61 * 62 * @see java.lang.module.SystemModuleFinder 63 * @see SystemModules 64 */ 65 public final class SystemModuleDescriptorPlugin implements TransformerPlugin { 66 // TODO: packager has the dependency on the plugin name 67 // Keep it as "--installed-modules" until packager removes such 68 // dependency (should not need to specify this plugin since it 69 // is enabled by default) 70 private static final String NAME = "installed-modules"; 71 private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME); 72 private boolean enabled; 73 74 public SystemModuleDescriptorPlugin() { 75 this.enabled = true; 76 } 77 78 @Override 79 public Set<PluginType> getType() { 80 return Collections.singleton(CATEGORY.TRANSFORMER); 81 } 82 83 @Override 84 public String getName() { 85 return NAME; 86 } 87 88 @Override 89 public String getDescription() { 90 return DESCRIPTION; 91 } 92 93 @Override 94 public Set<STATE> getState() { 95 return enabled ? EnumSet.of(STATE.AUTO_ENABLED, STATE.FUNCTIONAL) 96 : EnumSet.of(STATE.DISABLED); 97 } 98 99 @Override 100 public void configure(Map<String, String> config) { 101 if (config.containsKey(NAME)) { 102 enabled = false; 103 } 104 } 105 106 107 @Override 108 public void visit(Pool in, Pool out) { 109 if (!enabled) { 110 throw new PluginException(NAME + " was set"); 111 } 112 113 Builder builder = new Builder(); 114 115 // generate the byte code to create ModuleDescriptors 116 // skip parsing module-info.class and skip name check 117 for (Pool.Module module : in.getModules()) { 118 Pool.ModuleData data = module.get("module-info.class"); 119 if (data == null) { 120 // automatic module not supported yet 121 throw new PluginException("module-info.class not found for " + module.getName() + " module"); 122 } 123 assert module.getName().equals(data.getModule()); 124 try { 125 ByteArrayInputStream bain = new ByteArrayInputStream(data.getBytes()); 126 ModuleDescriptor md = ModuleDescriptor.read(bain); 127 validateNames(md); 128 129 Builder.ModuleDescriptorBuilder mbuilder = builder.module(md, module.getAllPackages()); 130 if (md.conceals().isEmpty() && 131 (md.exports().size() + md.conceals().size()) != module.getAllPackages().size()) { 132 // add ConcealedPackages attribute if not exist 133 bain.reset(); 134 ModuleInfoRewriter minfoWriter = new ModuleInfoRewriter(bain, mbuilder.conceals()); 135 // replace with the overridden version 136 data = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(), 137 minfoWriter.stream(), minfoWriter.size()); 138 } 139 out.add(data); 140 } catch (IOException e) { 141 throw new PluginException(e); 142 } 143 144 } 145 146 // Generate the new class 147 ClassWriter cwriter = builder.build(); 148 for (Pool.ModuleData data : in.getContent()) { 149 if (data.getPath().endsWith("module-info.class")) 150 continue; 151 152 if (builder.isOverriddenClass(data.getPath())) { 153 byte[] bytes = cwriter.toByteArray(); 154 Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(), 155 new ByteArrayInputStream(bytes), bytes.length); 156 out.add(ndata); 157 } else { 158 out.add(data); 159 } 160 } 161 } 162 163 /* 164 * Add ConcealedPackages attribute 165 */ 166 class ModuleInfoRewriter extends ByteArrayOutputStream { 167 final ModuleInfoExtender extender; 168 ModuleInfoRewriter(InputStream in, Set<String> conceals) throws IOException { 169 this.extender = ModuleInfoExtender.newExtender(in); 170 // Add ConcealedPackages attribute 171 this.extender.conceals(conceals); 172 this.extender.write(this); 173 } 174 175 InputStream stream() { 176 return new ByteArrayInputStream(buf); 177 } 178 } 179 180 void validateNames(ModuleDescriptor md) { 181 Checks.requireModuleName(md.name()); 182 for (Requires req : md.requires()) { 183 Checks.requireModuleName(req.name()); 184 } 185 for (Exports e : md.exports()) { 186 Checks.requirePackageName(e.source()); 187 if (e.isQualified()) 188 e.targets().forEach(Checks::requireModuleName); 189 } 190 for (Map.Entry<String, Provides> e : md.provides().entrySet()) { 191 String service = e.getKey(); 192 Provides provides = e.getValue(); 193 Checks.requireServiceTypeName(service); 194 Checks.requireServiceTypeName(provides.service()); 195 provides.providers().forEach(Checks::requireServiceProviderName); 196 } 197 for (String service : md.uses()) { 198 Checks.requireServiceTypeName(service); 199 } 200 for (String pn : md.conceals()) { 201 Checks.requirePackageName(pn); 202 } 203 } 204 205 /* 206 * Returns the initial capacity for a new Set or Map of the given size 207 * to avoid resizing. 208 */ 209 static final int initialCapacity(int size) { 210 if (size == 0) { 211 return 0; 212 } else { 213 // Adjust to try and get size/capacity as close to the 214 // HashSet/HashMap default load factor without going over. 215 return (int)(Math.ceil((double)size / 0.75)); 216 } 217 } 218 219 /** 220 * Builder of a new jdk.internal.module.SystemModules class 221 * to reconstitute ModuleDescriptor of the installed modules. 222 */ 223 static class Builder { 224 private static final String CLASSNAME = 225 "jdk/internal/module/SystemModules"; 226 private static final String MODULE_DESCRIPTOR_BUILDER = 227 "jdk/internal/module/Builder"; 228 private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE = 229 "[Ljava/lang/module/ModuleDescriptor;"; 230 231 // static variables in SystemModules class 232 private static final String MODULE_NAMES = "MODULE_NAMES"; 233 private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER"; 234 235 private static final int BUILDER_VAR = 0; 236 private static final int MD_VAR = 1; // variable for ModuleDescriptor 237 private static final int MODS_VAR = 2; // variable for Set<Modifier> 238 private static final int STRING_SET_VAR = 3; // variable for Set<String> 239 private static final int MAX_LOCAL_VARS = 256; 240 241 private final ClassWriter cw; 242 private MethodVisitor mv; 243 private int nextLocalVar = 4; 244 private int nextModulesIndex = 0; 245 246 // list of all ModuleDescriptorBuilders, invoked in turn when building. 247 private final List<ModuleDescriptorBuilder> builders = new ArrayList<>(); 248 249 // map Set<String> to a specialized builder to allow them to be 250 // deduplicated as they are requested 251 private final Map<Set<String>, StringSetBuilder> stringSets = new HashMap<>(); 252 253 public Builder() { 254 this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS+ClassWriter.COMPUTE_FRAMES); 255 } 256 257 /* 258 * static initializer initializing the static fields 259 * 260 * static Map<String, ModuleDescriptor> map = new HashMap<>(); 261 */ 262 private void clinit(int numModules, int numPackages) { 263 cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME, 264 null, "java/lang/Object", null); 265 266 // public static String[] MODULE_NAMES = new String[] {....}; 267 cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES, 268 "[Ljava/lang/String;", null, null) 269 .visitEnd(); 270 271 // public static int PACKAGES_IN_BOOT_LAYER; 272 cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT, 273 "I", null, numPackages) 274 .visitEnd(); 275 276 this.mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", 277 null, null); 278 mv.visitCode(); 279 280 // create the MODULE_NAMES array 281 pushInt(numModules); 282 mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 283 284 int index = 0; 285 for (ModuleDescriptorBuilder builder : builders) { 286 mv.visitInsn(DUP); // arrayref 287 pushInt(index++); 288 mv.visitLdcInsn(builder.md.name()); // value 289 mv.visitInsn(AASTORE); 290 } 291 292 mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES, 293 "[Ljava/lang/String;"); 294 295 mv.visitInsn(RETURN); 296 mv.visitMaxs(0, 0); 297 mv.visitEnd(); 298 299 } 300 301 /* 302 * Adds the given ModuleDescriptor to the installed module list, and 303 * prepares mapping from Set<String> to StringSetBuilders to emit an 304 * optimized number of string sets during build. 305 */ 306 public ModuleDescriptorBuilder module(ModuleDescriptor md, Set<String> packages) { 307 ModuleDescriptorBuilder builder = new ModuleDescriptorBuilder(md, packages); 308 builders.add(builder); 309 310 // exports 311 for (ModuleDescriptor.Exports e : md.exports()) { 312 if (e.isQualified()) { 313 stringSets.computeIfAbsent(e.targets(), s -> new StringSetBuilder(s)) 314 .increment(); 315 } 316 } 317 318 // provides 319 for (ModuleDescriptor.Provides p : md.provides().values()) { 320 stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s)) 321 .increment(); 322 } 323 324 // uses 325 stringSets.computeIfAbsent(md.uses(), s -> new StringSetBuilder(s)) 326 .increment(); 327 return builder; 328 } 329 330 /* 331 * Generate bytecode for SystemModules 332 */ 333 public ClassWriter build() { 334 int numModules = builders.size(); 335 int numPackages = 0; 336 for (ModuleDescriptorBuilder builder : builders) { 337 numPackages += builder.md.packages().size(); 338 } 339 340 this.clinit(numModules, numPackages); 341 this.mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC, 342 "modules", "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, 343 "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE, null); 344 mv.visitCode(); 345 pushInt(numModules); 346 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor"); 347 mv.visitVarInsn(ASTORE, MD_VAR); 348 349 for (ModuleDescriptorBuilder builder : builders) { 350 builder.build(); 351 } 352 mv.visitVarInsn(ALOAD, MD_VAR); 353 mv.visitInsn(ARETURN); 354 mv.visitMaxs(0, 0); 355 mv.visitEnd(); 356 return cw; 357 } 358 359 public boolean isOverriddenClass(String path) { 360 return path.equals("/java.base/" + CLASSNAME + ".class"); 361 } 362 363 void pushInt(int num) { 364 if (num <= 5) { 365 mv.visitInsn(ICONST_0 + num); 366 } else if (num < Byte.MAX_VALUE) { 367 mv.visitIntInsn(BIPUSH, num); 368 } else if (num < Short.MAX_VALUE) { 369 mv.visitIntInsn(SIPUSH, num); 370 } else { 371 throw new IllegalArgumentException("exceed limit: " + num); 372 } 373 } 374 375 class ModuleDescriptorBuilder { 376 static final String REQUIRES_MODIFIER_CLASSNAME = 377 "java/lang/module/ModuleDescriptor$Requires$Modifier"; 378 static final String REQUIRES_MODIFIER_TYPE = 379 "Ljava/lang/module/ModuleDescriptor$Requires$Modifier;"; 380 static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;"; 381 static final String REQUIRES_MODIFIER_STRING_SIG = 382 "(" + REQUIRES_MODIFIER_TYPE + "Ljava/lang/String;)" + BUILDER_TYPE; 383 static final String STRING_SET_SIG = 384 "(Ljava/lang/String;Ljava/util/Set;)" + BUILDER_TYPE; 385 static final String SET_STRING_SIG = 386 "(Ljava/util/Set;Ljava/lang/String;)" + BUILDER_TYPE; 387 static final String SET_SIG = 388 "(Ljava/util/Set;)" + BUILDER_TYPE; 389 static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE; 390 static final String STRING_STRING_SIG = 391 "(Ljava/lang/String;Ljava/lang/String;)" + BUILDER_TYPE; 392 393 final ModuleDescriptor md; 394 final Set<String> packages; 395 396 ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages) { 397 this.md = md; 398 this.packages = packages; 399 } 400 401 void newBuilder(String name, int reqs, int exports, int provides, 402 int conceals, int packages) { 403 mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER); 404 mv.visitInsn(DUP); 405 mv.visitLdcInsn(name); 406 pushInt(initialCapacity(reqs)); 407 pushInt(initialCapacity(exports)); 408 pushInt(initialCapacity(provides)); 409 pushInt(initialCapacity(conceals)); 410 pushInt(initialCapacity(packages)); 411 mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER, 412 "<init>", "(Ljava/lang/String;IIIII)V", false); 413 mv.visitVarInsn(ASTORE, BUILDER_VAR); 414 mv.visitVarInsn(ALOAD, BUILDER_VAR); 415 } 416 417 /* 418 * Returns the set of concealed packages from ModuleDescriptor, if present 419 * or compute it if the module oes not have ConcealedPackages attribute 420 */ 421 Set<String> conceals() { 422 Set<String> conceals = md.conceals(); 423 if (md.conceals().isEmpty() && 424 (md.exports().size() + md.conceals().size()) != packages.size()) { 425 Set<String> exports = md.exports().stream() 426 .map(Exports::source) 427 .collect(Collectors.toSet()); 428 conceals = packages.stream() 429 .filter(pn -> !exports.contains(pn)) 430 .collect(Collectors.toSet()); 431 } 432 433 if (conceals.size() + md.exports().size() != packages.size() && 434 // jdk.localedata may have concealed packages that don't exist 435 !md.name().equals("jdk.localedata")) { 436 throw new AssertionError(md.name() + ": conceals=" + conceals.size() + 437 ", exports=" + md.exports().size() + ", packages=" + packages.size()); 438 } 439 return conceals; 440 } 441 442 void build() { 443 newBuilder(md.name(), md.requires().size(), 444 md.exports().size(), 445 md.provides().size(), 446 conceals().size(), 447 conceals().size() + md.exports().size()); 448 449 // requires 450 for (ModuleDescriptor.Requires req : md.requires()) { 451 switch (req.modifiers().size()) { 452 case 0: 453 requires(req.name()); 454 break; 455 case 1: 456 ModuleDescriptor.Requires.Modifier mod = 457 req.modifiers().iterator().next(); 458 requires(mod, req.name()); 459 break; 460 default: 461 requires(req.modifiers(), req.name()); 462 } 463 } 464 465 // exports 466 for (ModuleDescriptor.Exports e : md.exports()) { 467 if (e.isQualified()) { 468 exports(e.source(), e.targets()); 469 } else { 470 exports(e.source()); 471 } 472 } 473 474 // uses 475 uses(md.uses()); 476 477 // provides 478 for (ModuleDescriptor.Provides p : md.provides().values()) { 479 provides(p.service(), p.providers()); 480 } 481 482 // concealed packages 483 for (String pn : conceals()) { 484 conceals(pn); 485 } 486 487 if (md.version().isPresent()) { 488 version(md.version().get()); 489 } 490 491 if (md.mainClass().isPresent()) { 492 mainClass(md.mainClass().get()); 493 } 494 495 putModuleDescriptor(); 496 } 497 498 /* 499 * Put ModuleDescriptor into the modules array 500 */ 501 void putModuleDescriptor() { 502 mv.visitVarInsn(ALOAD, MD_VAR); 503 pushInt(nextModulesIndex++); 504 mv.visitVarInsn(ALOAD, BUILDER_VAR); 505 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 506 "build", "()Ljava/lang/module/ModuleDescriptor;", false); 507 mv.visitInsn(AASTORE); 508 } 509 510 /* 511 * Invoke Builder.requires(String mn) 512 */ 513 void requires(String name) { 514 mv.visitVarInsn(ALOAD, BUILDER_VAR); 515 mv.visitLdcInsn(name); 516 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 517 "requires", STRING_SIG, false); 518 mv.visitInsn(POP); 519 } 520 521 /* 522 * Invoke Builder.requires(Modifier mod, String mn) 523 */ 524 void requires(ModuleDescriptor.Requires.Modifier mod, String name) { 525 mv.visitVarInsn(ALOAD, BUILDER_VAR); 526 mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, mod.name(), 527 REQUIRES_MODIFIER_TYPE); 528 mv.visitLdcInsn(name); 529 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 530 "requires", REQUIRES_MODIFIER_STRING_SIG, false); 531 mv.visitInsn(POP); 532 } 533 534 /* 535 * Invoke Builder.requires(Set<Modifier> mods, String mn) 536 * 537 * EnumSet<Modifier> mods = EnumSet.of(mod,....); 538 * Buidler.requires(mods, mn); 539 */ 540 void requires(Set<ModuleDescriptor.Requires.Modifier> mods, String name) { 541 mv.visitVarInsn(ALOAD, MODS_VAR); 542 String signature = "("; 543 for (ModuleDescriptor.Requires.Modifier m : mods) { 544 mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, m.name(), 545 REQUIRES_MODIFIER_TYPE); 546 signature += "Ljava/util/Enum;"; 547 } 548 signature += ")Ljava/util/EnumSet;"; 549 mv.visitMethodInsn(INVOKESTATIC, "java/util/EnumSet", "of", 550 signature, false); 551 mv.visitVarInsn(ASTORE, MODS_VAR); 552 mv.visitVarInsn(ALOAD, BUILDER_VAR); 553 mv.visitVarInsn(ALOAD, MODS_VAR); 554 mv.visitLdcInsn(name); 555 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 556 "requires", SET_STRING_SIG, false); 557 mv.visitInsn(POP); 558 } 559 560 /* 561 * Invoke Builder.exports(String pn) 562 */ 563 void exports(String pn) { 564 mv.visitVarInsn(ALOAD, BUILDER_VAR); 565 566 mv.visitLdcInsn(pn); 567 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 568 "exports", STRING_SIG, false); 569 mv.visitInsn(POP); 570 } 571 572 /* 573 * Invoke Builder.exports(String pn, Set<String> targets) 574 * 575 * Set<String> targets = new HashSet<>(); 576 * targets.add(t); 577 * : 578 * : 579 * Builder.exports(pn, targets); 580 */ 581 void exports(String pn, Set<String> targets) { 582 int varIndex = stringSets.get(targets).build(); 583 mv.visitVarInsn(ALOAD, BUILDER_VAR); 584 mv.visitLdcInsn(pn); 585 mv.visitVarInsn(ALOAD, varIndex); 586 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 587 "exports", STRING_SET_SIG, false); 588 mv.visitInsn(POP); 589 } 590 591 /* 592 * Invokes Builder.uses(Set<String> uses) 593 */ 594 void uses(Set<String> uses) { 595 int varIndex = stringSets.get(uses).build(); 596 mv.visitVarInsn(ALOAD, BUILDER_VAR); 597 mv.visitVarInsn(ALOAD, varIndex); 598 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 599 "uses", SET_SIG, false); 600 mv.visitInsn(POP); 601 } 602 603 /* 604 * Invoke Builder.provides(String service, Set<String> providers) 605 * 606 * Set<String> providers = new HashSet<>(); 607 * providers.add(impl); 608 * : 609 * : 610 * Builder.exports(service, providers); 611 */ 612 void provides(String service, Set<String> providers) { 613 int varIndex = stringSets.get(providers).build(); 614 mv.visitVarInsn(ALOAD, BUILDER_VAR); 615 mv.visitLdcInsn(service); 616 mv.visitVarInsn(ALOAD, varIndex); 617 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 618 "provides", STRING_SET_SIG, false); 619 mv.visitInsn(POP); 620 } 621 622 /* 623 * Invoke Builder.conceals(String pn) 624 */ 625 void conceals(String pn) { 626 mv.visitVarInsn(ALOAD, BUILDER_VAR); 627 mv.visitLdcInsn(pn); 628 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 629 "conceals", STRING_SIG, false); 630 mv.visitInsn(POP); 631 } 632 633 /* 634 * Invoke Builder.mainClass(String cn) 635 */ 636 void mainClass(String cn) { 637 mv.visitVarInsn(ALOAD, BUILDER_VAR); 638 mv.visitLdcInsn(cn); 639 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 640 "mainClass", STRING_SIG, false); 641 mv.visitInsn(POP); 642 } 643 644 /* 645 * Invoke Builder.version(Version v); 646 */ 647 void version(ModuleDescriptor.Version v) { 648 mv.visitVarInsn(ALOAD, BUILDER_VAR); 649 mv.visitLdcInsn(v.toString()); 650 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER, 651 "version", STRING_SIG, false); 652 mv.visitInsn(POP); 653 } 654 655 } 656 657 /* 658 * StringSetBuilder generates bytecode to create one single instance 659 * of HashSet for a given set of names and assign to a local variable 660 * slot. When there is only one single reference to a Set<String>, 661 * it will reuse STRING_SET_VAR for reference. For Set<String> with 662 * multiple references, it will use a new local variable. 663 */ 664 class StringSetBuilder { 665 final Set<String> names; 666 int refCount; 667 int localVarIndex; 668 StringSetBuilder(Set<String> names) { 669 this.names = names; 670 } 671 672 void increment() { 673 refCount++; 674 } 675 676 /* 677 * Build bytecode for the Set<String> represented by this builder, 678 * or get the local variable index of a previously generated set 679 * (in the local scope). 680 * 681 * @return local variable index of the generated set. 682 */ 683 int build() { 684 int index = localVarIndex; 685 if (localVarIndex == 0) { 686 // if non-empty and more than one set reference this builder, 687 // emit to a unique local 688 index = refCount == 1 ? STRING_SET_VAR 689 : nextLocalVar++; 690 if (index < MAX_LOCAL_VARS) { 691 localVarIndex = index; 692 } else { 693 // overflow: disable optimization and keep localVarIndex = 0 694 index = STRING_SET_VAR; 695 } 696 697 if (names.isEmpty()) { 698 mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections", 699 "emptySet", "()Ljava/util/Set;", false); 700 mv.visitVarInsn(ASTORE, index); 701 } else if (names.size() == 1) { 702 mv.visitLdcInsn(names.iterator().next()); 703 mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections", 704 "singleton", "(Ljava/lang/Object;)Ljava/util/Set;", false); 705 mv.visitVarInsn(ASTORE, index); 706 } else { 707 mv.visitTypeInsn(NEW, "java/util/HashSet"); 708 mv.visitInsn(DUP); 709 pushInt(initialCapacity(names.size())); 710 mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", 711 "<init>", "(I)V", false); 712 713 mv.visitVarInsn(ASTORE, index); 714 for (String t : names) { 715 mv.visitVarInsn(ALOAD, index); 716 mv.visitLdcInsn(t); 717 mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", 718 "add", "(Ljava/lang/Object;)Z", true); 719 mv.visitInsn(POP); 720 } 721 } 722 } 723 return index; 724 } 725 } 726 } 727 }