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