1 /*
2  * Copyright (c) 2014, 2019, 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.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.function.Supplier;
49 
50 import jdk.internal.access.JavaLangModuleAccess;
51 import jdk.internal.access.SharedSecrets;
52 
53 import static jdk.internal.module.ClassFileConstants.*;
54 
55 
56 /**
57  * Read module information from a {@code module-info} class file.
58  *
59  * @implNote The rationale for the hand-coded reader is startup performance
60  * and fine control over the throwing of InvalidModuleDescriptorException.
61  */
62 
63 public final class ModuleInfo {
64 
65     private final int JAVA_MIN_SUPPORTED_VERSION = 53;
66     private final int JAVA_MAX_SUPPORTED_VERSION = 58;
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
75     private final boolean parseHashes;
76 
ModuleInfo(Supplier<Set<String>> pf, boolean ph)77     private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
78         packageFinder = pf;
79         parseHashes = ph;
80     }
81 
ModuleInfo(Supplier<Set<String>> pf)82     private ModuleInfo(Supplier<Set<String>> pf) {
83         this(pf, true);
84     }
85 
86     /**
87      * A holder class for the ModuleDescriptor that is created by reading the
88      * Module and other standard class file attributes. It also holds the objects
89      * that represent the non-standard class file attributes that are read from
90      * the class file.
91      */
92     public static final class Attributes {
93         private final ModuleDescriptor descriptor;
94         private final ModuleTarget target;
95         private final ModuleHashes recordedHashes;
96         private final ModuleResolution moduleResolution;
Attributes(ModuleDescriptor descriptor, ModuleTarget target, ModuleHashes recordedHashes, ModuleResolution moduleResolution)97         Attributes(ModuleDescriptor descriptor,
98                    ModuleTarget target,
99                    ModuleHashes recordedHashes,
100                    ModuleResolution moduleResolution) {
101             this.descriptor = descriptor;
102             this.target = target;
103             this.recordedHashes = recordedHashes;
104             this.moduleResolution = moduleResolution;
105         }
descriptor()106         public ModuleDescriptor descriptor() {
107             return descriptor;
108         }
target()109         public ModuleTarget target() {
110             return target;
111         }
recordedHashes()112         public ModuleHashes recordedHashes() {
113             return recordedHashes;
114         }
moduleResolution()115         public ModuleResolution moduleResolution() {
116             return moduleResolution;
117         }
118     }
119 
120 
121     /**
122      * Reads a {@code module-info.class} from the given input stream.
123      *
124      * @throws InvalidModuleDescriptorException
125      * @throws IOException
126      */
read(InputStream in, Supplier<Set<String>> pf)127     public static Attributes read(InputStream in, Supplier<Set<String>> pf)
128         throws IOException
129     {
130         try {
131             return new ModuleInfo(pf).doRead(new DataInputStream(in));
132         } catch (IllegalArgumentException | IllegalStateException e) {
133             throw invalidModuleDescriptor(e.getMessage());
134         } catch (EOFException x) {
135             throw truncatedModuleDescriptor();
136         }
137     }
138 
139     /**
140      * Reads a {@code module-info.class} from the given byte buffer.
141      *
142      * @throws InvalidModuleDescriptorException
143      * @throws UncheckedIOException
144      */
read(ByteBuffer bb, Supplier<Set<String>> pf)145     public static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {
146         try {
147             return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
148         } catch (IllegalArgumentException | IllegalStateException e) {
149             throw invalidModuleDescriptor(e.getMessage());
150         } catch (EOFException x) {
151             throw truncatedModuleDescriptor();
152         } catch (IOException ioe) {
153             throw new UncheckedIOException(ioe);
154         }
155     }
156 
157     /**
158      * Reads a {@code module-info.class} from the given byte buffer
159      * but ignore the {@code ModuleHashes} attribute.
160      *
161      * @throws InvalidModuleDescriptorException
162      * @throws UncheckedIOException
163      */
readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf)164     public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {
165         try {
166             return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
167         } catch (IllegalArgumentException | IllegalStateException e) {
168             throw invalidModuleDescriptor(e.getMessage());
169         } catch (EOFException x) {
170             throw truncatedModuleDescriptor();
171         } catch (IOException ioe) {
172             throw new UncheckedIOException(ioe);
173         }
174     }
175 
176     /**
177      * Reads the input as a module-info class file.
178      *
179      * @throws IOException
180      * @throws InvalidModuleDescriptorException
181      * @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder
182      *         because an identifier is not a legal Java identifier, duplicate
183      *         exports, and many other reasons
184      */
doRead(DataInput in)185     private Attributes doRead(DataInput in) throws IOException {
186 
187         int magic = in.readInt();
188         if (magic != 0xCAFEBABE)
189             throw invalidModuleDescriptor("Bad magic number");
190 
191         int minor_version = in.readUnsignedShort();
192         int major_version = in.readUnsignedShort();
193         if (major_version < JAVA_MIN_SUPPORTED_VERSION ||
194                 major_version > JAVA_MAX_SUPPORTED_VERSION) {
195             throw invalidModuleDescriptor("Unsupported major.minor version "
196                                           + major_version + "." + minor_version);
197         }
198 
199         ConstantPool cpool = new ConstantPool(in);
200 
201         int access_flags = in.readUnsignedShort();
202         if (access_flags != ACC_MODULE)
203             throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
204 
205         int this_class = in.readUnsignedShort();
206         String mn = cpool.getClassName(this_class);
207         if (!"module-info".equals(mn))
208             throw invalidModuleDescriptor("this_class should be module-info");
209 
210         int super_class = in.readUnsignedShort();
211         if (super_class > 0)
212             throw invalidModuleDescriptor("bad #super_class");
213 
214         int interfaces_count = in.readUnsignedShort();
215         if (interfaces_count > 0)
216             throw invalidModuleDescriptor("Bad #interfaces");
217 
218         int fields_count = in.readUnsignedShort();
219         if (fields_count > 0)
220             throw invalidModuleDescriptor("Bad #fields");
221 
222         int methods_count = in.readUnsignedShort();
223         if (methods_count > 0)
224             throw invalidModuleDescriptor("Bad #methods");
225 
226         int attributes_count = in.readUnsignedShort();
227 
228         // the names of the attributes found in the class file
229         Set<String> attributes = new HashSet<>();
230 
231         Builder builder = null;
232         Set<String> allPackages = null;
233         String mainClass = null;
234         ModuleTarget moduleTarget = null;
235         ModuleHashes moduleHashes = null;
236         ModuleResolution moduleResolution = null;
237 
238         for (int i = 0; i < attributes_count ; i++) {
239             int name_index = in.readUnsignedShort();
240             String attribute_name = cpool.getUtf8(name_index);
241             int length = in.readInt();
242 
243             boolean added = attributes.add(attribute_name);
244             if (!added && isAttributeAtMostOnce(attribute_name)) {
245                 throw invalidModuleDescriptor("More than one "
246                                               + attribute_name + " attribute");
247             }
248 
249             switch (attribute_name) {
250 
251                 case MODULE :
252                     builder = readModuleAttribute(in, cpool, major_version);
253                     break;
254 
255                 case MODULE_PACKAGES :
256                     allPackages = readModulePackagesAttribute(in, cpool);
257                     break;
258 
259                 case MODULE_MAIN_CLASS :
260                     mainClass = readModuleMainClassAttribute(in, cpool);
261                     break;
262 
263                 case MODULE_TARGET :
264                     moduleTarget = readModuleTargetAttribute(in, cpool);
265                     break;
266 
267                 case MODULE_HASHES :
268                     if (parseHashes) {
269                         moduleHashes = readModuleHashesAttribute(in, cpool);
270                     } else {
271                         in.skipBytes(length);
272                     }
273                     break;
274 
275                 case MODULE_RESOLUTION :
276                     moduleResolution = readModuleResolution(in, cpool);
277                     break;
278 
279                 default:
280                     if (isAttributeDisallowed(attribute_name)) {
281                         throw invalidModuleDescriptor(attribute_name
282                                                       + " attribute not allowed");
283                     } else {
284                         in.skipBytes(length);
285                     }
286 
287             }
288         }
289 
290         // the Module attribute is required
291         if (builder == null) {
292             throw invalidModuleDescriptor(MODULE + " attribute not found");
293         }
294 
295         // ModuleMainClass  attribute
296         if (mainClass != null) {
297             builder.mainClass(mainClass);
298         }
299 
300         // If the ModulePackages attribute is not present then the packageFinder
301         // is used to find the set of packages
302         boolean usedPackageFinder = false;
303         if (allPackages == null && packageFinder != null) {
304             try {
305                 allPackages = packageFinder.get();
306             } catch (UncheckedIOException x) {
307                 throw x.getCause();
308             }
309             usedPackageFinder = true;
310         }
311         if (allPackages != null) {
312             Set<String> knownPackages = JLMA.packages(builder);
313             if (!allPackages.containsAll(knownPackages)) {
314                 Set<String> missingPackages = new HashSet<>(knownPackages);
315                 missingPackages.removeAll(allPackages);
316                 assert !missingPackages.isEmpty();
317                 String missingPackage = missingPackages.iterator().next();
318                 String tail;
319                 if (usedPackageFinder) {
320                     tail = " not found in module";
321                 } else {
322                     tail = " missing from ModulePackages class file attribute";
323                 }
324                 throw invalidModuleDescriptor("Package " + missingPackage + tail);
325 
326             }
327             builder.packages(allPackages);
328         }
329 
330         ModuleDescriptor descriptor = builder.build();
331         return new Attributes(descriptor,
332                               moduleTarget,
333                               moduleHashes,
334                               moduleResolution);
335     }
336 
337     /**
338      * Reads the Module attribute, returning the ModuleDescriptor.Builder to
339      * build the corresponding ModuleDescriptor.
340      */
readModuleAttribute(DataInput in, ConstantPool cpool, int major)341     private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major)
342         throws IOException
343     {
344         // module_name
345         int module_name_index = in.readUnsignedShort();
346         String mn = cpool.getModuleName(module_name_index);
347 
348         int module_flags = in.readUnsignedShort();
349 
350         Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();
351         boolean open = ((module_flags & ACC_OPEN) != 0);
352         if (open)
353             modifiers.add(ModuleDescriptor.Modifier.OPEN);
354         if ((module_flags & ACC_SYNTHETIC) != 0)
355             modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
356         if ((module_flags & ACC_MANDATED) != 0)
357             modifiers.add(ModuleDescriptor.Modifier.MANDATED);
358 
359         Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
360 
361         int module_version_index = in.readUnsignedShort();
362         if (module_version_index != 0) {
363             String vs = cpool.getUtf8(module_version_index);
364             builder.version(vs);
365         }
366 
367         int requires_count = in.readUnsignedShort();
368         boolean requiresJavaBase = false;
369         for (int i=0; i<requires_count; i++) {
370             int requires_index = in.readUnsignedShort();
371             String dn = cpool.getModuleName(requires_index);
372 
373             int requires_flags = in.readUnsignedShort();
374             Set<Requires.Modifier> mods;
375             if (requires_flags == 0) {
376                 mods = Set.of();
377             } else {
378                 mods = new HashSet<>();
379                 if ((requires_flags & ACC_TRANSITIVE) != 0)
380                     mods.add(Requires.Modifier.TRANSITIVE);
381                 if ((requires_flags & ACC_STATIC_PHASE) != 0)
382                     mods.add(Requires.Modifier.STATIC);
383                 if ((requires_flags & ACC_SYNTHETIC) != 0)
384                     mods.add(Requires.Modifier.SYNTHETIC);
385                 if ((requires_flags & ACC_MANDATED) != 0)
386                     mods.add(Requires.Modifier.MANDATED);
387             }
388 
389             int requires_version_index = in.readUnsignedShort();
390             if (requires_version_index == 0) {
391                 builder.requires(mods, dn);
392             } else {
393                 String vs = cpool.getUtf8(requires_version_index);
394                 JLMA.requires(builder, mods, dn, vs);
395             }
396 
397             if (dn.equals("java.base")) {
398                 if (major >= 54
399                     && (mods.contains(Requires.Modifier.TRANSITIVE)
400                         || mods.contains(Requires.Modifier.STATIC))) {
401                     String flagName;
402                     if (mods.contains(Requires.Modifier.TRANSITIVE)) {
403                         flagName = "ACC_TRANSITIVE";
404                     } else {
405                         flagName = "ACC_STATIC_PHASE";
406                     }
407                     throw invalidModuleDescriptor("The requires entry for java.base"
408                                                   + " has " + flagName + " set");
409                 }
410                 requiresJavaBase = true;
411             }
412         }
413         if (mn.equals("java.base")) {
414             if (requires_count > 0) {
415                 throw invalidModuleDescriptor("The requires table for java.base"
416                                               + " must be 0 length");
417             }
418         } else if (!requiresJavaBase) {
419             throw invalidModuleDescriptor("The requires table must have"
420                                           + " an entry for java.base");
421         }
422 
423         int exports_count = in.readUnsignedShort();
424         if (exports_count > 0) {
425             for (int i=0; i<exports_count; i++) {
426                 int exports_index = in.readUnsignedShort();
427                 String pkg = cpool.getPackageName(exports_index);
428 
429                 Set<Exports.Modifier> mods;
430                 int exports_flags = in.readUnsignedShort();
431                 if (exports_flags == 0) {
432                     mods = Set.of();
433                 } else {
434                     mods = new HashSet<>();
435                     if ((exports_flags & ACC_SYNTHETIC) != 0)
436                         mods.add(Exports.Modifier.SYNTHETIC);
437                     if ((exports_flags & ACC_MANDATED) != 0)
438                         mods.add(Exports.Modifier.MANDATED);
439                 }
440 
441                 int exports_to_count = in.readUnsignedShort();
442                 if (exports_to_count > 0) {
443                     Set<String> targets = new HashSet<>(exports_to_count);
444                     for (int j=0; j<exports_to_count; j++) {
445                         int exports_to_index = in.readUnsignedShort();
446                         String target = cpool.getModuleName(exports_to_index);
447                         if (!targets.add(target)) {
448                             throw invalidModuleDescriptor(pkg + " exported to "
449                                                           + target + " more than once");
450                         }
451                     }
452                     builder.exports(mods, pkg, targets);
453                 } else {
454                     builder.exports(mods, pkg);
455                 }
456             }
457         }
458 
459         int opens_count = in.readUnsignedShort();
460         if (opens_count > 0) {
461             if (open) {
462                 throw invalidModuleDescriptor("The opens table for an open"
463                                               + " module must be 0 length");
464             }
465             for (int i=0; i<opens_count; i++) {
466                 int opens_index = in.readUnsignedShort();
467                 String pkg = cpool.getPackageName(opens_index);
468 
469                 Set<Opens.Modifier> mods;
470                 int opens_flags = in.readUnsignedShort();
471                 if (opens_flags == 0) {
472                     mods = Set.of();
473                 } else {
474                     mods = new HashSet<>();
475                     if ((opens_flags & ACC_SYNTHETIC) != 0)
476                         mods.add(Opens.Modifier.SYNTHETIC);
477                     if ((opens_flags & ACC_MANDATED) != 0)
478                         mods.add(Opens.Modifier.MANDATED);
479                 }
480 
481                 int open_to_count = in.readUnsignedShort();
482                 if (open_to_count > 0) {
483                     Set<String> targets = new HashSet<>(open_to_count);
484                     for (int j=0; j<open_to_count; j++) {
485                         int opens_to_index = in.readUnsignedShort();
486                         String target = cpool.getModuleName(opens_to_index);
487                         if (!targets.add(target)) {
488                             throw invalidModuleDescriptor(pkg + " opened to "
489                                                           + target + " more than once");
490                         }
491                     }
492                     builder.opens(mods, pkg, targets);
493                 } else {
494                     builder.opens(mods, pkg);
495                 }
496             }
497         }
498 
499         int uses_count = in.readUnsignedShort();
500         if (uses_count > 0) {
501             for (int i=0; i<uses_count; i++) {
502                 int index = in.readUnsignedShort();
503                 String sn = cpool.getClassName(index);
504                 builder.uses(sn);
505             }
506         }
507 
508         int provides_count = in.readUnsignedShort();
509         if (provides_count > 0) {
510             for (int i=0; i<provides_count; i++) {
511                 int index = in.readUnsignedShort();
512                 String sn = cpool.getClassName(index);
513                 int with_count = in.readUnsignedShort();
514                 List<String> providers = new ArrayList<>(with_count);
515                 for (int j=0; j<with_count; j++) {
516                     index = in.readUnsignedShort();
517                     String pn = cpool.getClassName(index);
518                     if (!providers.add(pn)) {
519                         throw invalidModuleDescriptor(sn + " provides " + pn
520                                                       + " more than once");
521                     }
522                 }
523                 builder.provides(sn, providers);
524             }
525         }
526 
527         return builder;
528     }
529 
530     /**
531      * Reads the ModulePackages attribute
532      */
readModulePackagesAttribute(DataInput in, ConstantPool cpool)533     private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)
534         throws IOException
535     {
536         int package_count = in.readUnsignedShort();
537         Set<String> packages = new HashSet<>(package_count);
538         for (int i=0; i<package_count; i++) {
539             int index = in.readUnsignedShort();
540             String pn = cpool.getPackageName(index);
541             boolean added = packages.add(pn);
542             if (!added) {
543                 throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"
544                                               + "attribute more than once");
545             }
546         }
547         return packages;
548     }
549 
550     /**
551      * Reads the ModuleMainClass attribute
552      */
readModuleMainClassAttribute(DataInput in, ConstantPool cpool)553     private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
554         throws IOException
555     {
556         int index = in.readUnsignedShort();
557         return cpool.getClassName(index);
558     }
559 
560     /**
561      * Reads the ModuleTarget attribute
562      */
readModuleTargetAttribute(DataInput in, ConstantPool cpool)563     private ModuleTarget readModuleTargetAttribute(DataInput in, ConstantPool cpool)
564         throws IOException
565     {
566         String targetPlatform = null;
567 
568         int index = in.readUnsignedShort();
569         if (index != 0)
570             targetPlatform = cpool.getUtf8(index);
571 
572         return new ModuleTarget(targetPlatform);
573     }
574 
575     /**
576      * Reads the ModuleHashes attribute
577      */
readModuleHashesAttribute(DataInput in, ConstantPool cpool)578     private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
579         throws IOException
580     {
581         int algorithm_index = in.readUnsignedShort();
582         String algorithm = cpool.getUtf8(algorithm_index);
583 
584         int hash_count = in.readUnsignedShort();
585         Map<String, byte[]> map = new HashMap<>(hash_count);
586         for (int i=0; i<hash_count; i++) {
587             int module_name_index = in.readUnsignedShort();
588             String mn = cpool.getModuleName(module_name_index);
589             int hash_length = in.readUnsignedShort();
590             if (hash_length == 0) {
591                 throw invalidModuleDescriptor("hash_length == 0");
592             }
593             byte[] hash = new byte[hash_length];
594             in.readFully(hash);
595             map.put(mn, hash);
596         }
597 
598         return new ModuleHashes(algorithm, map);
599     }
600 
601     /**
602      * Reads the ModuleResolution attribute.
603      */
readModuleResolution(DataInput in, ConstantPool cpool)604     private ModuleResolution readModuleResolution(DataInput in,
605                                                   ConstantPool cpool)
606         throws IOException
607     {
608         int flags = in.readUnsignedShort();
609 
610         int reason = 0;
611         if ((flags & WARN_DEPRECATED) != 0)
612             reason = WARN_DEPRECATED;
613         if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0) {
614             if (reason != 0)
615                 throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
616             reason = WARN_DEPRECATED_FOR_REMOVAL;
617         }
618         if ((flags & WARN_INCUBATING) != 0) {
619             if (reason != 0)
620                 throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
621         }
622 
623         return new ModuleResolution(flags);
624     }
625 
626     /**
627      * Returns true if the given attribute can be present at most once
628      * in the class file. Returns false otherwise.
629      */
isAttributeAtMostOnce(String name)630     private static boolean isAttributeAtMostOnce(String name) {
631 
632         if (name.equals(MODULE) ||
633                 name.equals(SOURCE_FILE) ||
634                 name.equals(SDE) ||
635                 name.equals(MODULE_PACKAGES) ||
636                 name.equals(MODULE_MAIN_CLASS) ||
637                 name.equals(MODULE_TARGET) ||
638                 name.equals(MODULE_HASHES) ||
639                 name.equals(MODULE_RESOLUTION))
640             return true;
641 
642         return false;
643     }
644 
645     /**
646      * Return true if the given attribute name is the name of a pre-defined
647      * attribute in JVMS 4.7 that is not allowed in a module-info class.
648      */
isAttributeDisallowed(String name)649     private static boolean isAttributeDisallowed(String name) {
650         Set<String> notAllowed = predefinedNotAllowed;
651         if (notAllowed == null) {
652             notAllowed = Set.of(
653                     "ConstantValue",
654                     "Code",
655                     "Deprecated",
656                     "StackMapTable",
657                     "Exceptions",
658                     "EnclosingMethod",
659                     "Signature",
660                     "LineNumberTable",
661                     "LocalVariableTable",
662                     "LocalVariableTypeTable",
663                     "RuntimeVisibleParameterAnnotations",
664                     "RuntimeInvisibleParameterAnnotations",
665                     "RuntimeVisibleTypeAnnotations",
666                     "RuntimeInvisibleTypeAnnotations",
667                     "Synthetic",
668                     "AnnotationDefault",
669                     "BootstrapMethods",
670                     "MethodParameters");
671             predefinedNotAllowed = notAllowed;
672         }
673         return notAllowed.contains(name);
674     }
675 
676     // lazily created set the pre-defined attributes that are not allowed
677     private static volatile Set<String> predefinedNotAllowed;
678 
679 
680     /**
681      * The constant pool in a class file.
682      */
683     private static class ConstantPool {
684         static final int CONSTANT_Utf8 = 1;
685         static final int CONSTANT_Integer = 3;
686         static final int CONSTANT_Float = 4;
687         static final int CONSTANT_Long = 5;
688         static final int CONSTANT_Double = 6;
689         static final int CONSTANT_Class = 7;
690         static final int CONSTANT_String = 8;
691         static final int CONSTANT_Fieldref = 9;
692         static final int CONSTANT_Methodref = 10;
693         static final int CONSTANT_InterfaceMethodref = 11;
694         static final int CONSTANT_NameAndType = 12;
695         static final int CONSTANT_MethodHandle = 15;
696         static final int CONSTANT_MethodType = 16;
697         static final int CONSTANT_InvokeDynamic = 18;
698         static final int CONSTANT_Module = 19;
699         static final int CONSTANT_Package = 20;
700 
701         private static class Entry {
Entry(int tag)702             protected Entry(int tag) {
703                 this.tag = tag;
704             }
705             final int tag;
706         }
707 
708         private static class IndexEntry extends Entry {
IndexEntry(int tag, int index)709             IndexEntry(int tag, int index) {
710                 super(tag);
711                 this.index = index;
712             }
713             final int index;
714         }
715 
716         private static class Index2Entry extends Entry {
Index2Entry(int tag, int index1, int index2)717             Index2Entry(int tag, int index1, int index2) {
718                 super(tag);
719                 this.index1 = index1;
720                 this.index2 = index2;
721             }
722             final int index1,  index2;
723         }
724 
725         private static class ValueEntry extends Entry {
ValueEntry(int tag, Object value)726             ValueEntry(int tag, Object value) {
727                 super(tag);
728                 this.value = value;
729             }
730             final Object value;
731         }
732 
733         final Entry[] pool;
734 
ConstantPool(DataInput in)735         ConstantPool(DataInput in) throws IOException {
736             int count = in.readUnsignedShort();
737             pool = new Entry[count];
738 
739             for (int i = 1; i < count; i++) {
740                 int tag = in.readUnsignedByte();
741                 switch (tag) {
742 
743                     case CONSTANT_Utf8:
744                         String svalue = in.readUTF();
745                         pool[i] = new ValueEntry(tag, svalue);
746                         break;
747 
748                     case CONSTANT_Class:
749                     case CONSTANT_Package:
750                     case CONSTANT_Module:
751                     case CONSTANT_String:
752                         int index = in.readUnsignedShort();
753                         pool[i] = new IndexEntry(tag, index);
754                         break;
755 
756                     case CONSTANT_Double:
757                         double dvalue = in.readDouble();
758                         pool[i] = new ValueEntry(tag, dvalue);
759                         i++;
760                         break;
761 
762                     case CONSTANT_Fieldref:
763                     case CONSTANT_InterfaceMethodref:
764                     case CONSTANT_Methodref:
765                     case CONSTANT_InvokeDynamic:
766                     case CONSTANT_NameAndType:
767                         int index1 = in.readUnsignedShort();
768                         int index2 = in.readUnsignedShort();
769                         pool[i] = new Index2Entry(tag, index1, index2);
770                         break;
771 
772                     case CONSTANT_MethodHandle:
773                         int refKind = in.readUnsignedByte();
774                         index = in.readUnsignedShort();
775                         pool[i] = new Index2Entry(tag, refKind, index);
776                         break;
777 
778                     case CONSTANT_MethodType:
779                         index = in.readUnsignedShort();
780                         pool[i] = new IndexEntry(tag, index);
781                         break;
782 
783                     case CONSTANT_Float:
784                         float fvalue = in.readFloat();
785                         pool[i] = new ValueEntry(tag, fvalue);
786                         break;
787 
788                     case CONSTANT_Integer:
789                         int ivalue = in.readInt();
790                         pool[i] = new ValueEntry(tag, ivalue);
791                         break;
792 
793                     case CONSTANT_Long:
794                         long lvalue = in.readLong();
795                         pool[i] = new ValueEntry(tag, lvalue);
796                         i++;
797                         break;
798 
799                     default:
800                         throw invalidModuleDescriptor("Bad constant pool entry: "
801                                                       + i);
802                 }
803             }
804         }
805 
getClassName(int index)806         String getClassName(int index) {
807             checkIndex(index);
808             Entry e = pool[index];
809             if (e.tag != CONSTANT_Class) {
810                 throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
811                                               + index);
812             }
813             String value = getUtf8(((IndexEntry) e).index);
814             checkUnqualifiedName("CONSTANT_Class", index, value);
815             return value.replace('/', '.');  // internal form -> binary name
816         }
817 
getPackageName(int index)818         String getPackageName(int index) {
819             checkIndex(index);
820             Entry e = pool[index];
821             if (e.tag != CONSTANT_Package) {
822                 throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
823                                               + index);
824             }
825             String value = getUtf8(((IndexEntry) e).index);
826             checkUnqualifiedName("CONSTANT_Package", index, value);
827             return value.replace('/', '.');  // internal form -> binary name
828         }
829 
getModuleName(int index)830         String getModuleName(int index) {
831             checkIndex(index);
832             Entry e = pool[index];
833             if (e.tag != CONSTANT_Module) {
834                 throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
835                                               + index);
836             }
837             String value = getUtf8(((IndexEntry) e).index);
838             return decodeModuleName(index, value);
839         }
840 
getUtf8(int index)841         String getUtf8(int index) {
842             checkIndex(index);
843             Entry e = pool[index];
844             if (e.tag != CONSTANT_Utf8) {
845                 throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "
846                                               + index);
847             }
848             return (String) (((ValueEntry) e).value);
849         }
850 
checkIndex(int index)851         void checkIndex(int index) {
852             if (index < 1 || index >= pool.length)
853                 throw invalidModuleDescriptor("Index into constant pool out of range");
854         }
855 
checkUnqualifiedName(String what, int index, String value)856         void checkUnqualifiedName(String what, int index, String value) {
857             int len = value.length();
858             if (len == 0) {
859                 throw invalidModuleDescriptor(what + " at entry " + index
860                                               + " has zero length");
861             }
862             for (int i=0; i<len; i++) {
863                 char c = value.charAt(i);
864                 if (c == '.' || c == ';' || c == '[') {
865                     throw invalidModuleDescriptor(what + " at entry " + index
866                                                   + " has illegal character: '"
867                                                   + c + "'");
868                 }
869             }
870         }
871 
872         /**
873          * "Decode" a module name that has been read from the constant pool.
874          */
decodeModuleName(int index, String value)875         String decodeModuleName(int index, String value) {
876             int len = value.length();
877             if (len == 0) {
878                 throw invalidModuleDescriptor("CONSTANT_Module at entry "
879                                               + index + " is zero length");
880             }
881             int i = 0;
882             while (i < len) {
883                 int cp = value.codePointAt(i);
884                 if (cp == ':' || cp == '@' || cp < 0x20) {
885                     throw invalidModuleDescriptor("CONSTANT_Module at entry "
886                                                   + index + " has illegal character: "
887                                                   + Character.getName(cp));
888                 }
889 
890                 // blackslash is the escape character
891                 if (cp == '\\')
892                     return decodeModuleName(index, i, value);
893 
894                 i += Character.charCount(cp);
895             }
896             return value;
897         }
898 
899         /**
900          * "Decode" a module name that has been read from the constant pool and
901          * partly checked for illegal characters (up to position {@code i}).
902          */
decodeModuleName(int index, int i, String value)903         String decodeModuleName(int index, int i, String value) {
904             StringBuilder sb = new StringBuilder();
905 
906             // copy the code points that have been checked
907             int j = 0;
908             while (j < i) {
909                 int cp = value.codePointAt(j);
910                 sb.appendCodePoint(cp);
911                 j += Character.charCount(cp);
912             }
913 
914             // decode from position {@code i} to end
915             int len = value.length();
916             while (i < len) {
917                 int cp = value.codePointAt(i);
918                 if (cp == ':' || cp == '@' || cp < 0x20) {
919                     throw invalidModuleDescriptor("CONSTANT_Module at entry "
920                                                   + index + " has illegal character: "
921                                                   + Character.getName(cp));
922                 }
923 
924                 // blackslash is the escape character
925                 if (cp == '\\') {
926                     j = i + Character.charCount(cp);
927                     if (j >= len) {
928                         throw invalidModuleDescriptor("CONSTANT_Module at entry "
929                                                        + index + " has illegal "
930                                                        + "escape sequence");
931                     }
932                     int next = value.codePointAt(j);
933                     if (next != '\\' && next != ':' && next != '@') {
934                         throw invalidModuleDescriptor("CONSTANT_Module at entry "
935                                                       + index + " has illegal "
936                                                       + "escape sequence");
937                     }
938                     sb.appendCodePoint(next);
939                     i += Character.charCount(next);
940                 } else {
941                     sb.appendCodePoint(cp);
942                 }
943 
944                 i += Character.charCount(cp);
945             }
946             return sb.toString();
947         }
948     }
949 
950     /**
951      * A DataInput implementation that reads from a ByteBuffer.
952      */
953     private static class DataInputWrapper implements DataInput {
954         private final ByteBuffer bb;
955 
DataInputWrapper(ByteBuffer bb)956         DataInputWrapper(ByteBuffer bb) {
957             this.bb = bb;
958         }
959 
960         @Override
readFully(byte b[])961         public void readFully(byte b[]) throws IOException {
962             readFully(b, 0, b.length);
963         }
964 
965         @Override
readFully(byte b[], int off, int len)966         public void readFully(byte b[], int off, int len) throws IOException {
967             try {
968                 bb.get(b, off, len);
969             } catch (BufferUnderflowException e) {
970                 throw new EOFException(e.getMessage());
971             }
972         }
973 
974         @Override
skipBytes(int n)975         public int skipBytes(int n) {
976             int skip = Math.min(n, bb.remaining());
977             bb.position(bb.position() + skip);
978             return skip;
979         }
980 
981         @Override
readBoolean()982         public boolean readBoolean() throws IOException {
983             try {
984                 int ch = bb.get();
985                 return (ch != 0);
986             } catch (BufferUnderflowException e) {
987                 throw new EOFException(e.getMessage());
988             }
989         }
990 
991         @Override
readByte()992         public byte readByte() throws IOException {
993             try {
994                 return bb.get();
995             } catch (BufferUnderflowException e) {
996                 throw new EOFException(e.getMessage());
997             }
998         }
999 
1000         @Override
readUnsignedByte()1001         public int readUnsignedByte() throws IOException {
1002             try {
1003                 return ((int) bb.get()) & 0xff;
1004             } catch (BufferUnderflowException e) {
1005                 throw new EOFException(e.getMessage());
1006             }
1007         }
1008 
1009         @Override
readShort()1010         public short readShort() throws IOException {
1011             try {
1012                 return bb.getShort();
1013             } catch (BufferUnderflowException e) {
1014                 throw new EOFException(e.getMessage());
1015             }
1016         }
1017 
1018         @Override
readUnsignedShort()1019         public int readUnsignedShort() throws IOException {
1020             try {
1021                 return ((int) bb.getShort()) & 0xffff;
1022             } catch (BufferUnderflowException e) {
1023                 throw new EOFException(e.getMessage());
1024             }
1025         }
1026 
1027         @Override
readChar()1028         public char readChar() throws IOException {
1029             try {
1030                 return bb.getChar();
1031             } catch (BufferUnderflowException e) {
1032                 throw new EOFException(e.getMessage());
1033             }
1034         }
1035 
1036         @Override
readInt()1037         public int readInt() throws IOException {
1038             try {
1039                 return bb.getInt();
1040             } catch (BufferUnderflowException e) {
1041                 throw new EOFException(e.getMessage());
1042             }
1043         }
1044 
1045         @Override
readLong()1046         public long readLong() throws IOException {
1047             try {
1048                 return bb.getLong();
1049             } catch (BufferUnderflowException e) {
1050                 throw new EOFException(e.getMessage());
1051             }
1052         }
1053 
1054         @Override
readFloat()1055         public float readFloat() throws IOException {
1056             try {
1057                 return bb.getFloat();
1058             } catch (BufferUnderflowException e) {
1059                 throw new EOFException(e.getMessage());
1060             }
1061         }
1062 
1063         @Override
readDouble()1064         public double readDouble() throws IOException {
1065             try {
1066                 return bb.getDouble();
1067             } catch (BufferUnderflowException e) {
1068                 throw new EOFException(e.getMessage());
1069             }
1070         }
1071 
1072         @Override
readLine()1073         public String readLine() {
1074             throw new RuntimeException("not implemented");
1075         }
1076 
1077         @Override
readUTF()1078         public String readUTF() throws IOException {
1079             // ### Need to measure the performance and feasibility of using
1080             // the UTF-8 decoder instead.
1081             return DataInputStream.readUTF(this);
1082         }
1083     }
1084 
1085     /**
1086      * Returns an InvalidModuleDescriptorException with the given detail
1087      * message
1088      */
1089     private static InvalidModuleDescriptorException
invalidModuleDescriptor(String msg)1090     invalidModuleDescriptor(String msg) {
1091         return new InvalidModuleDescriptorException(msg);
1092     }
1093 
1094     /**
1095      * Returns an InvalidModuleDescriptorException with a detail message to
1096      * indicate that the class file is truncated.
1097      */
truncatedModuleDescriptor()1098     private static InvalidModuleDescriptorException truncatedModuleDescriptor() {
1099         return invalidModuleDescriptor("Truncated module-info.class");
1100     }
1101 
1102 }
1103