< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java

Print this page


   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


  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.lang.module.ModuleDescriptor;
  32 import java.lang.module.ModuleDescriptor.Exports;
  33 import java.lang.module.ModuleDescriptor.Opens;
  34 import java.lang.module.ModuleDescriptor.Provides;
  35 import java.lang.module.ModuleDescriptor.Requires;
  36 import java.lang.module.ModuleDescriptor.Version;
  37 import java.util.ArrayList;
  38 import java.util.Collection;
  39 import java.util.EnumSet;
  40 import java.util.HashMap;
  41 import java.util.HashSet;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Set;
  45 import java.util.TreeSet;
  46 import java.util.function.IntSupplier;
  47 
  48 import jdk.internal.module.Checks;


  49 import jdk.internal.module.ModuleHashes;
  50 import jdk.internal.module.ModuleInfo.Attributes;
  51 import jdk.internal.module.ModuleInfoExtender;
  52 import jdk.internal.module.ModuleResolution;
  53 import jdk.internal.module.SystemModules;



  54 import jdk.internal.org.objectweb.asm.ClassWriter;
  55 import jdk.internal.org.objectweb.asm.MethodVisitor;
  56 import jdk.internal.org.objectweb.asm.Opcodes;
  57 
  58 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  59 
  60 import jdk.tools.jlink.internal.ModuleSorter;
  61 import jdk.tools.jlink.plugin.PluginException;
  62 import jdk.tools.jlink.plugin.ResourcePool;
  63 import jdk.tools.jlink.plugin.Plugin;
  64 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
  65 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  66 
  67 /**
  68  * Jlink plugin to reconstitute module descriptors for system modules.
  69  * It will extend module-info.class with ModulePackages attribute,
  70  * if not present. It also determines the number of packages of
  71  * the boot layer at link time.
  72  *
  73  * This plugin will override jdk.internal.module.SystemModules class


  92         return NAME;
  93     }
  94 
  95     @Override
  96     public String getDescription() {
  97         return DESCRIPTION;
  98     }
  99 
 100     @Override
 101     public Set<State> getState() {
 102         return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
 103                        : EnumSet.of(State.DISABLED);
 104     }
 105 
 106     @Override
 107     public boolean hasArguments() {
 108         return true;
 109     }
 110 
 111     @Override





 112     public void configure(Map<String, String> config) {
 113         String arg = config.get(NAME);
 114         if (arg != null) {
 115             if (arg.equals("retainModuleTarget")) {
 116                 retainModuleTarget = true;
 117             } else {
 118                 throw new IllegalArgumentException(NAME + ": " + arg);
 119             }
 120         }
 121     }
 122 
 123     @Override
 124     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 125         if (!enabled) {
 126             throw new PluginException(NAME + " was set");
 127         }
 128 
 129         SystemModulesClassGenerator generator =
 130             new SystemModulesClassGenerator(retainModuleTarget);
 131 


 154         });
 155 
 156         // Generate the new class
 157         ClassWriter cwriter = generator.getClassWriter();
 158         in.entries().forEach(data -> {
 159             if (data.path().endsWith("module-info.class"))
 160                 return;
 161             if (generator.isOverriddenClass(data.path())) {
 162                 byte[] bytes = cwriter.toByteArray();
 163                 ResourcePoolEntry ndata = data.copyWithContent(bytes);
 164                 out.add(ndata);
 165             } else {
 166                 out.add(data);
 167             }
 168         });
 169 
 170         return out.build();
 171     }
 172 
 173     static class ModuleInfo {

 174         private final Attributes attrs;
 175         private final Set<String> packages;
 176         private final ByteArrayInputStream bain;
 177         private final boolean dropModuleTarget;

 178         private ModuleDescriptor descriptor;  // may be different that the original one
 179 
 180         ModuleInfo(byte[] bytes, Set<String> packages, boolean dropModuleTarget)
 181             throws IOException
 182         {
 183             this.bain = new ByteArrayInputStream(bytes);
 184             this.packages = packages;
 185 
 186             this.attrs = jdk.internal.module.ModuleInfo.read(bain, null);




 187             this.descriptor = attrs.descriptor();
 188             if (descriptor.isAutomatic()) {
 189                 throw new InternalError("linking automatic module is not supported");
 190             }
 191 
 192             if (dropModuleTarget) {


 193                 // drop target attribute only if any OS property is present

 194                 this.dropModuleTarget =
 195                     descriptor.osName().isPresent() ||
 196                     descriptor.osArch().isPresent() ||
 197                     descriptor.osVersion().isPresent();
 198             } else {
 199                 this.dropModuleTarget = false;
 200             }
 201         }
 202 
 203         String moduleName() {
 204             return attrs.descriptor().name();
 205         }
 206 
 207         ModuleDescriptor descriptor() {
 208             return descriptor;
 209         }
 210 
 211 
 212         Set<String> packages() {
 213             return packages;


 259          * Validates if exported and open packages are present
 260          */
 261         void validatePackages() {
 262             Set<String> nonExistPackages = new TreeSet<>();
 263             descriptor.exports().stream()
 264                 .map(Exports::source)
 265                 .filter(pn -> !packages.contains(pn))
 266                 .forEach(nonExistPackages::add);
 267 
 268             descriptor.opens().stream()
 269                 .map(Opens::source)
 270                 .filter(pn -> !packages.contains(pn))
 271                 .forEach(nonExistPackages::add);
 272 
 273             if (!nonExistPackages.isEmpty()) {
 274                 throw new PluginException("Packages that are exported or open in "
 275                     + descriptor.name() + " are not present: " + nonExistPackages);
 276             }
 277         }
 278 






















 279         /**
 280          * Returns true if module-info.class should be written
 281          * 1. add ModulePackages attribute if not present; or
 282          * 2. drop ModuleTarget attribute except java.base
 283          */
 284         boolean shouldRewrite() {
 285             return shouldAddModulePackages() || shouldDropModuleTarget();
 286         }
 287 
 288         boolean shouldAddModulePackages() {
 289             return (descriptor.packages().isEmpty() && packages.size() > 0);
 290         }
 291 
 292         boolean shouldDropModuleTarget() {
 293             return dropModuleTarget &&
 294                         (descriptor.osName().isPresent() ||
 295                          descriptor.osArch().isPresent() ||
 296                          descriptor.osVersion().isPresent());
 297         }
 298 
 299         /**
 300          * Returns the bytes for the module-info.class with ModulePackages
 301          * if it contains at least one package
 302          */
 303         byte[] getBytes() throws IOException {
 304             bain.reset();
 305 
 306             // add ModulePackages attribute if not exist
 307             if (shouldRewrite()) {
 308                 ModuleInfoRewriter rewriter = new ModuleInfoRewriter(bain);
 309                 if (shouldAddModulePackages()) {
 310                     rewriter.addModulePackages(packages);
 311                 }
 312                 if (shouldDropModuleTarget()) {
 313                     rewriter.dropModuleTarget();
 314                 }
 315                 // rewritten module descriptor
 316                 byte[] bytes = rewriter.getBytes();
 317                 try (ByteArrayInputStream bain = new ByteArrayInputStream(bytes)) {
 318                      this.descriptor = ModuleDescriptor.read(bain);
 319                 }
 320                 return bytes;
 321             } else {
 322                 return bain.readAllBytes();

 323             }
 324         }
 325 








 326         class ModuleInfoRewriter extends ByteArrayOutputStream {
 327             final ModuleInfoExtender extender;
 328             ModuleInfoRewriter(InputStream in) {
 329                 this.extender = ModuleInfoExtender.newExtender(in);
 330             }
 331 
 332             void addModulePackages(Set<String> packages) {
 333                 // Add ModulePackages attribute
 334                 if (packages.size() > 0) {
 335                     extender.packages(packages);
 336                 }
 337             }
 338 
 339             void dropModuleTarget() {
 340                 extender.targetPlatform("", "", "");
 341             }
 342 
 343             byte[] getBytes() throws IOException {
 344                 extender.write(this);
 345                 return buf;


   1 /*
   2  * Copyright (c) 2015, 2017, 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


  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.lang.module.ModuleDescriptor;
  32 import java.lang.module.ModuleDescriptor.Exports;
  33 import java.lang.module.ModuleDescriptor.Opens;
  34 import java.lang.module.ModuleDescriptor.Provides;
  35 import java.lang.module.ModuleDescriptor.Requires;
  36 import java.lang.module.ModuleDescriptor.Version;
  37 import java.util.ArrayList;
  38 import java.util.Collection;
  39 import java.util.EnumSet;
  40 import java.util.HashMap;
  41 import java.util.HashSet;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Set;
  45 import java.util.TreeSet;
  46 import java.util.function.IntSupplier;
  47 
  48 import jdk.internal.module.Checks;
  49 import jdk.internal.module.ClassFileAttributes;
  50 import jdk.internal.module.ClassFileConstants;
  51 import jdk.internal.module.ModuleHashes;
  52 import jdk.internal.module.ModuleInfo.Attributes;
  53 import jdk.internal.module.ModuleInfoExtender;
  54 import jdk.internal.module.ModuleResolution;
  55 import jdk.internal.module.SystemModules;
  56 import jdk.internal.org.objectweb.asm.Attribute;
  57 import jdk.internal.org.objectweb.asm.ClassReader;
  58 import jdk.internal.org.objectweb.asm.ClassVisitor;
  59 import jdk.internal.org.objectweb.asm.ClassWriter;
  60 import jdk.internal.org.objectweb.asm.MethodVisitor;
  61 import jdk.internal.org.objectweb.asm.Opcodes;
  62 
  63 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  64 
  65 import jdk.tools.jlink.internal.ModuleSorter;
  66 import jdk.tools.jlink.plugin.PluginException;
  67 import jdk.tools.jlink.plugin.ResourcePool;
  68 import jdk.tools.jlink.plugin.Plugin;
  69 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
  70 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  71 
  72 /**
  73  * Jlink plugin to reconstitute module descriptors for system modules.
  74  * It will extend module-info.class with ModulePackages attribute,
  75  * if not present. It also determines the number of packages of
  76  * the boot layer at link time.
  77  *
  78  * This plugin will override jdk.internal.module.SystemModules class


  97         return NAME;
  98     }
  99 
 100     @Override
 101     public String getDescription() {
 102         return DESCRIPTION;
 103     }
 104 
 105     @Override
 106     public Set<State> getState() {
 107         return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
 108                        : EnumSet.of(State.DISABLED);
 109     }
 110 
 111     @Override
 112     public boolean hasArguments() {
 113         return true;
 114     }
 115 
 116     @Override
 117     public String getArgumentsDescription() {
 118         return PluginsResourceBundle.getArgument(NAME);
 119     }
 120 
 121     @Override
 122     public void configure(Map<String, String> config) {
 123         String arg = config.get(NAME);
 124         if (arg != null) {
 125             if (arg.equals("retainModuleTarget")) {
 126                 retainModuleTarget = true;
 127             } else {
 128                 throw new IllegalArgumentException(NAME + ": " + arg);
 129             }
 130         }
 131     }
 132 
 133     @Override
 134     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 135         if (!enabled) {
 136             throw new PluginException(NAME + " was set");
 137         }
 138 
 139         SystemModulesClassGenerator generator =
 140             new SystemModulesClassGenerator(retainModuleTarget);
 141 


 164         });
 165 
 166         // Generate the new class
 167         ClassWriter cwriter = generator.getClassWriter();
 168         in.entries().forEach(data -> {
 169             if (data.path().endsWith("module-info.class"))
 170                 return;
 171             if (generator.isOverriddenClass(data.path())) {
 172                 byte[] bytes = cwriter.toByteArray();
 173                 ResourcePoolEntry ndata = data.copyWithContent(bytes);
 174                 out.add(ndata);
 175             } else {
 176                 out.add(data);
 177             }
 178         });
 179 
 180         return out.build();
 181     }
 182 
 183     static class ModuleInfo {
 184         private final ByteArrayInputStream bain;
 185         private final Attributes attrs;
 186         private final Set<String> packages;

 187         private final boolean dropModuleTarget;
 188         private final boolean addModulePackages;
 189         private ModuleDescriptor descriptor;  // may be different that the original one
 190 
 191         ModuleInfo(byte[] bytes, Set<String> packages, boolean dropModuleTarget)
 192             throws IOException
 193         {
 194             this.bain = new ByteArrayInputStream(bytes);
 195             this.packages = packages;

 196             this.attrs = jdk.internal.module.ModuleInfo.read(bain, null);
 197             // If ModulePackages attribute is present, the packages from this
 198             // module descriptor returns the packages in that attribute.
 199             // If it's not present, ModuleDescriptor::packages only contains
 200             // the exported and open packages from module-info.class
 201             this.descriptor = attrs.descriptor();
 202             if (descriptor.isAutomatic()) {
 203                 throw new InternalError("linking automatic module is not supported");
 204             }
 205 
 206             // add ModulePackages attribute if this module contains some packages
 207             // and ModulePackages is not present
 208             this.addModulePackages = packages.size() > 0 && !hasModulePackages();
 209             // drop target attribute only if any OS property is present
 210             if (dropModuleTarget) {
 211                 this.dropModuleTarget =
 212                     descriptor.osName().isPresent() ||
 213                     descriptor.osArch().isPresent() ||
 214                     descriptor.osVersion().isPresent();
 215             } else {
 216                 this.dropModuleTarget = false;
 217             }
 218         }
 219 
 220         String moduleName() {
 221             return attrs.descriptor().name();
 222         }
 223 
 224         ModuleDescriptor descriptor() {
 225             return descriptor;
 226         }
 227 
 228 
 229         Set<String> packages() {
 230             return packages;


 276          * Validates if exported and open packages are present
 277          */
 278         void validatePackages() {
 279             Set<String> nonExistPackages = new TreeSet<>();
 280             descriptor.exports().stream()
 281                 .map(Exports::source)
 282                 .filter(pn -> !packages.contains(pn))
 283                 .forEach(nonExistPackages::add);
 284 
 285             descriptor.opens().stream()
 286                 .map(Opens::source)
 287                 .filter(pn -> !packages.contains(pn))
 288                 .forEach(nonExistPackages::add);
 289 
 290             if (!nonExistPackages.isEmpty()) {
 291                 throw new PluginException("Packages that are exported or open in "
 292                     + descriptor.name() + " are not present: " + nonExistPackages);
 293             }
 294         }
 295 
 296         boolean hasModulePackages() throws IOException {
 297             Set<String> attrTypes = new HashSet<>();
 298             ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
 299                 @Override
 300                 public void visitAttribute(Attribute attr) {
 301                     attrTypes.add(attr.type);
 302                 }
 303             };
 304 
 305             // prototype of attributes that should be parsed
 306             Attribute[] attrs = new Attribute[] {
 307                 new ClassFileAttributes.ModulePackagesAttribute()
 308             };
 309 
 310             try (InputStream in = getInputStream()) {
 311                 // parse module-info.class
 312                 ClassReader cr = new ClassReader(in);
 313                 cr.accept(cv, attrs, 0);
 314                 return attrTypes.contains(ClassFileConstants.MODULE_PACKAGES);
 315             }
 316         }
 317 
 318         /**
 319          * Returns true if module-info.class should be written
 320          * 1. add ModulePackages attribute if not present; or
 321          * 2. drop ModuleTarget attribute except java.base
 322          */
 323         boolean shouldRewrite() {
 324             return addModulePackages || dropModuleTarget;











 325         }
 326 
 327         /**
 328          * Returns the bytes for the module-info.class with ModulePackages
 329          * attribute added and/or with ModuleTarget attribute dropped.
 330          */
 331         byte[] getBytes() throws IOException {
 332             try (InputStream in = getInputStream()) {


 333                 if (shouldRewrite()) {
 334                     ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in);
 335                     if (addModulePackages) {
 336                         rewriter.addModulePackages(packages);
 337                     }
 338                     if (dropModuleTarget) {
 339                         rewriter.dropModuleTarget();
 340                     }
 341                     // rewritten module descriptor
 342                     byte[] bytes = rewriter.getBytes();
 343                     try (ByteArrayInputStream bain = new ByteArrayInputStream(bytes)) {
 344                         this.descriptor = ModuleDescriptor.read(bain);
 345                     }
 346                     return bytes;
 347                 } else {
 348                     return in.readAllBytes();
 349                 }
 350             }
 351         }
 352 
 353         /*
 354          * Returns the input stream of the module-info.class
 355          */
 356         InputStream getInputStream() {
 357             bain.reset();
 358             return bain;
 359         }
 360 
 361         class ModuleInfoRewriter extends ByteArrayOutputStream {
 362             final ModuleInfoExtender extender;
 363             ModuleInfoRewriter(InputStream in) {
 364                 this.extender = ModuleInfoExtender.newExtender(in);
 365             }
 366 
 367             void addModulePackages(Set<String> packages) {
 368                 // Add ModulePackages attribute
 369                 if (packages.size() > 0) {
 370                     extender.packages(packages);
 371                 }
 372             }
 373 
 374             void dropModuleTarget() {
 375                 extender.targetPlatform("", "", "");
 376             }
 377 
 378             byte[] getBytes() throws IOException {
 379                 extender.write(this);
 380                 return buf;


< prev index next >