1 /*
2 * Copyright (c) 2014, 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
26 package jdk.internal.module;
27
28 import java.io.DataInput;
29 import java.io.DataInputStream;
30 import java.io.EOFException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.UncheckedIOException;
34 import java.lang.module.InvalidModuleDescriptorException;
35 import java.lang.module.ModuleDescriptor;
36 import java.lang.module.ModuleDescriptor.Builder;
37 import java.lang.module.ModuleDescriptor.Requires;
38 import java.lang.module.ModuleDescriptor.Exports;
39 import java.lang.module.ModuleDescriptor.Opens;
40 import java.lang.module.ModuleDescriptor.Version;
41 import java.nio.ByteBuffer;
42 import java.nio.BufferUnderflowException;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.function.Supplier;
51
52 import jdk.internal.misc.JavaLangModuleAccess;
53 import jdk.internal.misc.SharedSecrets;
54 import jdk.internal.module.ModuleResolution;
55
56 import static jdk.internal.module.ClassFileConstants.*;
57
58
59 /**
60 * Read module information from a {@code module-info} class file.
61 *
62 * @implNote The rationale for the hand-coded reader is startup performance
63 * and fine control over the throwing of InvalidModuleDescriptorException.
64 */
65
66 public final class ModuleInfo {
67
68 private static final JavaLangModuleAccess JLMA
69 = SharedSecrets.getJavaLangModuleAccess();
70
71 // supplies the set of packages when ModulePackages attribute not present
72 private final Supplier<Set<String>> packageFinder;
73
74 // indicates if the ModuleHashes attribute should be parsed
204 throw invalidModuleDescriptor("bad #super_class");
205
206 int interfaces_count = in.readUnsignedShort();
207 if (interfaces_count > 0)
208 throw invalidModuleDescriptor("Bad #interfaces");
209
210 int fields_count = in.readUnsignedShort();
211 if (fields_count > 0)
212 throw invalidModuleDescriptor("Bad #fields");
213
214 int methods_count = in.readUnsignedShort();
215 if (methods_count > 0)
216 throw invalidModuleDescriptor("Bad #methods");
217
218 int attributes_count = in.readUnsignedShort();
219
220 // the names of the attributes found in the class file
221 Set<String> attributes = new HashSet<>();
222
223 Builder builder = null;
224 Set<String> packages = null;
225 String mainClass = null;
226 String[] osValues = null;
227 ModuleHashes hashes = null;
228 ModuleResolution moduleResolution = null;
229
230 for (int i = 0; i < attributes_count ; i++) {
231 int name_index = in.readUnsignedShort();
232 String attribute_name = cpool.getUtf8(name_index);
233 int length = in.readInt();
234
235 boolean added = attributes.add(attribute_name);
236 if (!added && isAttributeAtMostOnce(attribute_name)) {
237 throw invalidModuleDescriptor("More than one "
238 + attribute_name + " attribute");
239 }
240
241 switch (attribute_name) {
242
243 case MODULE :
244 builder = readModuleAttribute(in, cpool);
245 break;
246
247 case MODULE_PACKAGES :
248 packages = readModulePackagesAttribute(in, cpool);
249 break;
250
251 case MODULE_MAIN_CLASS :
252 mainClass = readModuleMainClassAttribute(in, cpool);
253 break;
254
255 case MODULE_TARGET :
256 osValues = readModuleTargetAttribute(in, cpool);
257 break;
258
259 case MODULE_HASHES :
260 if (parseHashes) {
261 hashes = readModuleHashesAttribute(in, cpool);
262 } else {
263 in.skipBytes(length);
264 }
265 break;
266
267 case MODULE_RESOLUTION :
268 moduleResolution = readModuleResolution(in, cpool);
269 break;
270
271 default:
272 if (isAttributeDisallowed(attribute_name)) {
273 throw invalidModuleDescriptor(attribute_name
274 + " attribute not allowed");
275 } else {
276 in.skipBytes(length);
277 }
278
279 }
280 }
281
282 // the Module attribute is required
283 if (builder == null) {
284 throw invalidModuleDescriptor(MODULE + " attribute not found");
285 }
286
287 // If the ModulePackages attribute is not present then the packageFinder
288 // is used to find the set of packages
289 boolean usedPackageFinder = false;
290 if (packages == null && packageFinder != null) {
291 try {
292 packages = new HashSet<>(packageFinder.get());
293 } catch (UncheckedIOException x) {
294 throw x.getCause();
295 }
296 usedPackageFinder = true;
297 }
298 if (packages != null) {
299 Set<String> exportedPackages = JLMA.exportedPackages(builder);
300 Set<String> openPackages = JLMA.openPackages(builder);
301 if (packages.containsAll(exportedPackages)
302 && packages.containsAll(openPackages)) {
303 packages.removeAll(exportedPackages);
304 packages.removeAll(openPackages);
305 } else {
306 // the set of packages is not complete
307 Set<String> exportedAndOpenPackages = new HashSet<>();
308 exportedAndOpenPackages.addAll(exportedPackages);
309 exportedAndOpenPackages.addAll(openPackages);
310 for (String pn : exportedAndOpenPackages) {
311 if (!packages.contains(pn)) {
312 String tail;
313 if (usedPackageFinder) {
314 tail = " not found by package finder";
315 } else {
316 tail = " missing from ModulePackages attribute";
317 }
318 throw invalidModuleDescriptor("Package " + pn + tail);
319 }
320 }
321 assert false; // should not get here
322 }
323 builder.contains(packages);
324 }
325
326 if (mainClass != null)
327 builder.mainClass(mainClass);
328 if (osValues != null) {
329 if (osValues[0] != null) builder.osName(osValues[0]);
330 if (osValues[1] != null) builder.osArch(osValues[1]);
331 if (osValues[2] != null) builder.osVersion(osValues[2]);
332 }
333
334 ModuleDescriptor descriptor = builder.build();
335 return new Attributes(descriptor, hashes, moduleResolution);
336 }
337
338 /**
339 * Reads the Module attribute, returning the ModuleDescriptor.Builder to
340 * build the corresponding ModuleDescriptor.
341 */
342 private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
343 throws IOException
344 {
345 // module_name
346 int module_name_index = in.readUnsignedShort();
347 String mn = cpool.getModuleName(module_name_index);
348
349 int module_flags = in.readUnsignedShort();
350 boolean open = ((module_flags & ACC_OPEN) != 0);
351 boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0);
352
353 Builder builder = JLMA.newModuleBuilder(mn, false, open, synthetic);
354
355 int module_version_index = in.readUnsignedShort();
356 if (module_version_index != 0) {
357 String vs = cpool.getUtf8(module_version_index);
358 builder.version(vs);
359 }
360
361 int requires_count = in.readUnsignedShort();
362 boolean requiresJavaBase = false;
363 for (int i=0; i<requires_count; i++) {
364 int requires_index = in.readUnsignedShort();
365 String dn = cpool.getModuleName(requires_index);
366
367 int requires_flags = in.readUnsignedShort();
368 Set<Requires.Modifier> mods;
369 if (requires_flags == 0) {
370 mods = Collections.emptySet();
371 } else {
372 mods = new HashSet<>();
373 if ((requires_flags & ACC_TRANSITIVE) != 0)
374 mods.add(Requires.Modifier.TRANSITIVE);
375 if ((requires_flags & ACC_STATIC_PHASE) != 0)
376 mods.add(Requires.Modifier.STATIC);
377 if ((requires_flags & ACC_SYNTHETIC) != 0)
378 mods.add(Requires.Modifier.SYNTHETIC);
379 if ((requires_flags & ACC_MANDATED) != 0)
380 mods.add(Requires.Modifier.MANDATED);
381 }
382
383 int requires_version_index = in.readUnsignedShort();
384 Version compiledVersion = null;
385 if (requires_version_index != 0) {
386 String vs = cpool.getUtf8(requires_version_index);
387 compiledVersion = Version.parse(vs);
388 }
389
390 if (compiledVersion == null) {
391 builder.requires(mods, dn);
392 } else {
393 builder.requires(mods, dn, compiledVersion);
394 }
395
396 if (dn.equals("java.base"))
397 requiresJavaBase = true;
398 }
399 if (mn.equals("java.base")) {
400 if (requires_count > 0) {
401 throw invalidModuleDescriptor("The requires table for java.base"
402 + " must be 0 length");
403 }
404 } else if (!requiresJavaBase) {
405 throw invalidModuleDescriptor("The requires table must have"
406 + " an entry for java.base");
407 }
408
409 int exports_count = in.readUnsignedShort();
410 if (exports_count > 0) {
411 for (int i=0; i<exports_count; i++) {
412 int exports_index = in.readUnsignedShort();
413 String pkg = cpool.getPackageName(exports_index);
612 * Returns true if the given attribute can be present at most once
613 * in the class file. Returns false otherwise.
614 */
615 private static boolean isAttributeAtMostOnce(String name) {
616
617 if (name.equals(MODULE) ||
618 name.equals(SOURCE_FILE) ||
619 name.equals(SDE) ||
620 name.equals(MODULE_PACKAGES) ||
621 name.equals(MODULE_MAIN_CLASS) ||
622 name.equals(MODULE_TARGET) ||
623 name.equals(MODULE_HASHES) ||
624 name.equals(MODULE_RESOLUTION))
625 return true;
626
627 return false;
628 }
629
630 /**
631 * Return true if the given attribute name is the name of a pre-defined
632 * attribute that is not allowed in the class file.
633 *
634 * Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and
635 * Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
636 */
637 private static boolean isAttributeDisallowed(String name) {
638 Set<String> notAllowed = predefinedNotAllowed;
639 if (notAllowed == null) {
640 notAllowed = Set.of(
641 "ConstantValue",
642 "Code",
643 "StackMapTable",
644 "Exceptions",
645 "EnclosingMethod",
646 "Signature",
647 "LineNumberTable",
648 "LocalVariableTable",
649 "LocalVariableTypeTable",
650 "RuntimeVisibleParameterAnnotations",
651 "RuntimeInvisibleParameterAnnotations",
652 "RuntimeVisibleTypeAnnotations",
653 "RuntimeInvisibleTypeAnnotations",
654 "Synthetic",
655 "AnnotationDefault",
656 "BootstrapMethods",
657 "MethodParameters");
658 predefinedNotAllowed = notAllowed;
659 }
660 return notAllowed.contains(name);
661 }
662
|
1 /*
2 * Copyright (c) 2014, 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
23 * questions.
24 */
25
26 package jdk.internal.module;
27
28 import java.io.DataInput;
29 import java.io.DataInputStream;
30 import java.io.EOFException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.UncheckedIOException;
34 import java.lang.module.InvalidModuleDescriptorException;
35 import java.lang.module.ModuleDescriptor;
36 import java.lang.module.ModuleDescriptor.Builder;
37 import java.lang.module.ModuleDescriptor.Requires;
38 import java.lang.module.ModuleDescriptor.Exports;
39 import java.lang.module.ModuleDescriptor.Opens;
40 import java.nio.ByteBuffer;
41 import java.nio.BufferUnderflowException;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.function.Supplier;
50
51 import jdk.internal.misc.JavaLangModuleAccess;
52 import jdk.internal.misc.SharedSecrets;
53
54 import static jdk.internal.module.ClassFileConstants.*;
55
56
57 /**
58 * Read module information from a {@code module-info} class file.
59 *
60 * @implNote The rationale for the hand-coded reader is startup performance
61 * and fine control over the throwing of InvalidModuleDescriptorException.
62 */
63
64 public final class ModuleInfo {
65
66 private static final JavaLangModuleAccess JLMA
67 = SharedSecrets.getJavaLangModuleAccess();
68
69 // supplies the set of packages when ModulePackages attribute not present
70 private final Supplier<Set<String>> packageFinder;
71
72 // indicates if the ModuleHashes attribute should be parsed
202 throw invalidModuleDescriptor("bad #super_class");
203
204 int interfaces_count = in.readUnsignedShort();
205 if (interfaces_count > 0)
206 throw invalidModuleDescriptor("Bad #interfaces");
207
208 int fields_count = in.readUnsignedShort();
209 if (fields_count > 0)
210 throw invalidModuleDescriptor("Bad #fields");
211
212 int methods_count = in.readUnsignedShort();
213 if (methods_count > 0)
214 throw invalidModuleDescriptor("Bad #methods");
215
216 int attributes_count = in.readUnsignedShort();
217
218 // the names of the attributes found in the class file
219 Set<String> attributes = new HashSet<>();
220
221 Builder builder = null;
222 Set<String> allPackages = null;
223 String mainClass = null;
224 String[] osValues = null;
225 ModuleHashes hashes = null;
226 ModuleResolution moduleResolution = null;
227
228 for (int i = 0; i < attributes_count ; i++) {
229 int name_index = in.readUnsignedShort();
230 String attribute_name = cpool.getUtf8(name_index);
231 int length = in.readInt();
232
233 boolean added = attributes.add(attribute_name);
234 if (!added && isAttributeAtMostOnce(attribute_name)) {
235 throw invalidModuleDescriptor("More than one "
236 + attribute_name + " attribute");
237 }
238
239 switch (attribute_name) {
240
241 case MODULE :
242 builder = readModuleAttribute(in, cpool);
243 break;
244
245 case MODULE_PACKAGES :
246 allPackages = readModulePackagesAttribute(in, cpool);
247 break;
248
249 case MODULE_MAIN_CLASS :
250 mainClass = readModuleMainClassAttribute(in, cpool);
251 break;
252
253 case MODULE_TARGET :
254 osValues = readModuleTargetAttribute(in, cpool);
255 break;
256
257 case MODULE_HASHES :
258 if (parseHashes) {
259 hashes = readModuleHashesAttribute(in, cpool);
260 } else {
261 in.skipBytes(length);
262 }
263 break;
264
265 case MODULE_RESOLUTION :
266 moduleResolution = readModuleResolution(in, cpool);
267 break;
268
269 default:
270 if (isAttributeDisallowed(attribute_name)) {
271 throw invalidModuleDescriptor(attribute_name
272 + " attribute not allowed");
273 } else {
274 in.skipBytes(length);
275 }
276
277 }
278 }
279
280 // the Module attribute is required
281 if (builder == null) {
282 throw invalidModuleDescriptor(MODULE + " attribute not found");
283 }
284
285 // ModuleMainClass and ModuleTarget attributes
286 if (mainClass != null) {
287 builder.mainClass(mainClass);
288 }
289 if (osValues != null) {
290 if (osValues[0] != null) builder.osName(osValues[0]);
291 if (osValues[1] != null) builder.osArch(osValues[1]);
292 if (osValues[2] != null) builder.osVersion(osValues[2]);
293 }
294
295 // If the ModulePackages attribute is not present then the packageFinder
296 // is used to find the set of packages
297 boolean usedPackageFinder = false;
298 if (allPackages == null && packageFinder != null) {
299 try {
300 allPackages = packageFinder.get();
301 } catch (UncheckedIOException x) {
302 throw x.getCause();
303 }
304 usedPackageFinder = true;
305 }
306 if (allPackages != null) {
307 Set<String> knownPackages = JLMA.packages(builder);
308 if (!allPackages.containsAll(knownPackages)) {
309 Set<String> missingPackages = new HashSet<>(knownPackages);
310 missingPackages.removeAll(allPackages);
311 assert !missingPackages.isEmpty();
312 String missingPackage = missingPackages.iterator().next();
313 String tail;
314 if (usedPackageFinder) {
315 tail = " not found in module";
316 } else {
317 tail = " missing from ModulePackages class file attribute";
318 }
319 throw invalidModuleDescriptor("Package " + missingPackage + tail);
320
321 }
322 builder.packages(allPackages);
323 }
324
325 ModuleDescriptor descriptor = builder.build();
326 return new Attributes(descriptor, hashes, moduleResolution);
327 }
328
329 /**
330 * Reads the Module attribute, returning the ModuleDescriptor.Builder to
331 * build the corresponding ModuleDescriptor.
332 */
333 private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
334 throws IOException
335 {
336 // module_name
337 int module_name_index = in.readUnsignedShort();
338 String mn = cpool.getModuleName(module_name_index);
339
340 int module_flags = in.readUnsignedShort();
341
342 Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();
343 boolean open = ((module_flags & ACC_OPEN) != 0);
344 if (open)
345 modifiers.add(ModuleDescriptor.Modifier.OPEN);
346 if ((module_flags & ACC_SYNTHETIC) != 0)
347 modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
348 if ((module_flags & ACC_MANDATED) != 0)
349 modifiers.add(ModuleDescriptor.Modifier.MANDATED);
350
351 Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
352
353 int module_version_index = in.readUnsignedShort();
354 if (module_version_index != 0) {
355 String vs = cpool.getUtf8(module_version_index);
356 builder.version(vs);
357 }
358
359 int requires_count = in.readUnsignedShort();
360 boolean requiresJavaBase = false;
361 for (int i=0; i<requires_count; i++) {
362 int requires_index = in.readUnsignedShort();
363 String dn = cpool.getModuleName(requires_index);
364
365 int requires_flags = in.readUnsignedShort();
366 Set<Requires.Modifier> mods;
367 if (requires_flags == 0) {
368 mods = Collections.emptySet();
369 } else {
370 mods = new HashSet<>();
371 if ((requires_flags & ACC_TRANSITIVE) != 0)
372 mods.add(Requires.Modifier.TRANSITIVE);
373 if ((requires_flags & ACC_STATIC_PHASE) != 0)
374 mods.add(Requires.Modifier.STATIC);
375 if ((requires_flags & ACC_SYNTHETIC) != 0)
376 mods.add(Requires.Modifier.SYNTHETIC);
377 if ((requires_flags & ACC_MANDATED) != 0)
378 mods.add(Requires.Modifier.MANDATED);
379 }
380
381 int requires_version_index = in.readUnsignedShort();
382 if (requires_version_index == 0) {
383 builder.requires(mods, dn);
384 } else {
385 String vs = cpool.getUtf8(requires_version_index);
386 JLMA.requires(builder, mods, dn, vs);
387 }
388
389 if (dn.equals("java.base"))
390 requiresJavaBase = true;
391 }
392 if (mn.equals("java.base")) {
393 if (requires_count > 0) {
394 throw invalidModuleDescriptor("The requires table for java.base"
395 + " must be 0 length");
396 }
397 } else if (!requiresJavaBase) {
398 throw invalidModuleDescriptor("The requires table must have"
399 + " an entry for java.base");
400 }
401
402 int exports_count = in.readUnsignedShort();
403 if (exports_count > 0) {
404 for (int i=0; i<exports_count; i++) {
405 int exports_index = in.readUnsignedShort();
406 String pkg = cpool.getPackageName(exports_index);
605 * Returns true if the given attribute can be present at most once
606 * in the class file. Returns false otherwise.
607 */
608 private static boolean isAttributeAtMostOnce(String name) {
609
610 if (name.equals(MODULE) ||
611 name.equals(SOURCE_FILE) ||
612 name.equals(SDE) ||
613 name.equals(MODULE_PACKAGES) ||
614 name.equals(MODULE_MAIN_CLASS) ||
615 name.equals(MODULE_TARGET) ||
616 name.equals(MODULE_HASHES) ||
617 name.equals(MODULE_RESOLUTION))
618 return true;
619
620 return false;
621 }
622
623 /**
624 * Return true if the given attribute name is the name of a pre-defined
625 * attribute in JVMS 4.7 that is not allowed in a module-info class.
626 */
627 private static boolean isAttributeDisallowed(String name) {
628 Set<String> notAllowed = predefinedNotAllowed;
629 if (notAllowed == null) {
630 notAllowed = Set.of(
631 "ConstantValue",
632 "Code",
633 "Deprecated",
634 "StackMapTable",
635 "Exceptions",
636 "EnclosingMethod",
637 "Signature",
638 "LineNumberTable",
639 "LocalVariableTable",
640 "LocalVariableTypeTable",
641 "RuntimeVisibleParameterAnnotations",
642 "RuntimeInvisibleParameterAnnotations",
643 "RuntimeVisibleTypeAnnotations",
644 "RuntimeInvisibleTypeAnnotations",
645 "Synthetic",
646 "AnnotationDefault",
647 "BootstrapMethods",
648 "MethodParameters");
649 predefinedNotAllowed = notAllowed;
650 }
651 return notAllowed.contains(name);
652 }
653
|