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;
|