Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
41159 views
/*1* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.DataInput;28import java.io.DataInputStream;29import java.io.EOFException;30import java.io.IOException;31import java.io.InputStream;32import java.io.UncheckedIOException;33import java.lang.module.InvalidModuleDescriptorException;34import java.lang.module.ModuleDescriptor;35import java.lang.module.ModuleDescriptor.Builder;36import java.lang.module.ModuleDescriptor.Requires;37import java.lang.module.ModuleDescriptor.Exports;38import java.lang.module.ModuleDescriptor.Opens;39import java.nio.ByteBuffer;40import java.nio.BufferUnderflowException;41import java.util.ArrayList;42import java.util.HashMap;43import java.util.HashSet;44import java.util.List;45import java.util.Map;46import java.util.Set;47import java.util.function.Supplier;4849import jdk.internal.access.JavaLangModuleAccess;50import jdk.internal.access.SharedSecrets;51import jdk.internal.misc.VM;5253import static jdk.internal.module.ClassFileConstants.*;545556/**57* Read module information from a {@code module-info} class file.58*59* @implNote The rationale for the hand-coded reader is startup performance60* and fine control over the throwing of InvalidModuleDescriptorException.61*/6263public final class ModuleInfo {6465private static final JavaLangModuleAccess JLMA66= SharedSecrets.getJavaLangModuleAccess();6768// supplies the set of packages when ModulePackages attribute not present69private final Supplier<Set<String>> packageFinder;7071// indicates if the ModuleHashes attribute should be parsed72private final boolean parseHashes;7374private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {75packageFinder = pf;76parseHashes = ph;77}7879private ModuleInfo(Supplier<Set<String>> pf) {80this(pf, true);81}8283/**84* A holder class for the ModuleDescriptor that is created by reading the85* Module and other standard class file attributes. It also holds the objects86* that represent the non-standard class file attributes that are read from87* the class file.88*/89public static final class Attributes {90private final ModuleDescriptor descriptor;91private final ModuleTarget target;92private final ModuleHashes recordedHashes;93private final ModuleResolution moduleResolution;94Attributes(ModuleDescriptor descriptor,95ModuleTarget target,96ModuleHashes recordedHashes,97ModuleResolution moduleResolution) {98this.descriptor = descriptor;99this.target = target;100this.recordedHashes = recordedHashes;101this.moduleResolution = moduleResolution;102}103public ModuleDescriptor descriptor() {104return descriptor;105}106public ModuleTarget target() {107return target;108}109public ModuleHashes recordedHashes() {110return recordedHashes;111}112public ModuleResolution moduleResolution() {113return moduleResolution;114}115}116117118/**119* Reads a {@code module-info.class} from the given input stream.120*121* @throws InvalidModuleDescriptorException122* @throws IOException123*/124public static Attributes read(InputStream in, Supplier<Set<String>> pf)125throws IOException126{127try {128return new ModuleInfo(pf).doRead(new DataInputStream(in));129} catch (IllegalArgumentException | IllegalStateException e) {130throw invalidModuleDescriptor(e.getMessage());131} catch (EOFException x) {132throw truncatedModuleDescriptor();133}134}135136/**137* Reads a {@code module-info.class} from the given byte buffer.138*139* @throws InvalidModuleDescriptorException140* @throws UncheckedIOException141*/142public static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {143try {144return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));145} catch (IllegalArgumentException | IllegalStateException e) {146throw invalidModuleDescriptor(e.getMessage());147} catch (EOFException x) {148throw truncatedModuleDescriptor();149} catch (IOException ioe) {150throw new UncheckedIOException(ioe);151}152}153154/**155* Reads a {@code module-info.class} from the given byte buffer156* but ignore the {@code ModuleHashes} attribute.157*158* @throws InvalidModuleDescriptorException159* @throws UncheckedIOException160*/161public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {162try {163return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));164} catch (IllegalArgumentException | IllegalStateException e) {165throw invalidModuleDescriptor(e.getMessage());166} catch (EOFException x) {167throw truncatedModuleDescriptor();168} catch (IOException ioe) {169throw new UncheckedIOException(ioe);170}171}172173/**174* Reads the input as a module-info class file.175*176* @throws IOException177* @throws InvalidModuleDescriptorException178* @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder179* because an identifier is not a legal Java identifier, duplicate180* exports, and many other reasons181*/182private Attributes doRead(DataInput input) throws IOException {183var in = new CountingDataInput(input);184185int magic = in.readInt();186if (magic != 0xCAFEBABE)187throw invalidModuleDescriptor("Bad magic number");188189int minor_version = in.readUnsignedShort();190int major_version = in.readUnsignedShort();191if (!VM.isSupportedModuleDescriptorVersion(major_version, minor_version)) {192throw invalidModuleDescriptor("Unsupported major.minor version "193+ major_version + "." + minor_version);194}195196ConstantPool cpool = new ConstantPool(in);197198int access_flags = in.readUnsignedShort();199if (access_flags != ACC_MODULE)200throw invalidModuleDescriptor("access_flags should be ACC_MODULE");201202int this_class = in.readUnsignedShort();203String mn = cpool.getClassName(this_class);204if (!"module-info".equals(mn))205throw invalidModuleDescriptor("this_class should be module-info");206207int super_class = in.readUnsignedShort();208if (super_class > 0)209throw invalidModuleDescriptor("bad #super_class");210211int interfaces_count = in.readUnsignedShort();212if (interfaces_count > 0)213throw invalidModuleDescriptor("Bad #interfaces");214215int fields_count = in.readUnsignedShort();216if (fields_count > 0)217throw invalidModuleDescriptor("Bad #fields");218219int methods_count = in.readUnsignedShort();220if (methods_count > 0)221throw invalidModuleDescriptor("Bad #methods");222223int attributes_count = in.readUnsignedShort();224225// the names of the attributes found in the class file226Set<String> attributes = new HashSet<>();227228Builder builder = null;229Set<String> allPackages = null;230String mainClass = null;231ModuleTarget moduleTarget = null;232ModuleHashes moduleHashes = null;233ModuleResolution moduleResolution = null;234235for (int i = 0; i < attributes_count ; i++) {236int name_index = in.readUnsignedShort();237String attribute_name = cpool.getUtf8(name_index);238int length = in.readInt();239240boolean added = attributes.add(attribute_name);241if (!added && isAttributeAtMostOnce(attribute_name)) {242throw invalidModuleDescriptor("More than one "243+ attribute_name + " attribute");244}245246long initialPosition = in.count();247248switch (attribute_name) {249case MODULE :250builder = readModuleAttribute(in, cpool, major_version);251break;252253case MODULE_PACKAGES :254allPackages = readModulePackagesAttribute(in, cpool);255break;256257case MODULE_MAIN_CLASS :258mainClass = readModuleMainClassAttribute(in, cpool);259break;260261case MODULE_TARGET :262moduleTarget = readModuleTargetAttribute(in, cpool);263break;264265case MODULE_HASHES :266if (parseHashes) {267moduleHashes = readModuleHashesAttribute(in, cpool);268} else {269in.skipBytes(length);270}271break;272273case MODULE_RESOLUTION :274moduleResolution = readModuleResolution(in, cpool);275break;276277default:278if (isAttributeDisallowed(attribute_name)) {279throw invalidModuleDescriptor(attribute_name280+ " attribute not allowed");281} else {282in.skipBytes(length);283}284}285286long newPosition = in.count();287if ((newPosition - initialPosition) != length) {288// attribute length does not match actual attribute size289throw invalidModuleDescriptor("Attribute " + attribute_name290+ " does not match its expected length");291}292293}294295// the Module attribute is required296if (builder == null) {297throw invalidModuleDescriptor(MODULE + " attribute not found");298}299300// ModuleMainClass attribute301if (mainClass != null) {302builder.mainClass(mainClass);303}304305// If the ModulePackages attribute is not present then the packageFinder306// is used to find the set of packages307boolean usedPackageFinder = false;308if (allPackages == null && packageFinder != null) {309try {310allPackages = packageFinder.get();311} catch (UncheckedIOException x) {312throw x.getCause();313}314usedPackageFinder = true;315}316if (allPackages != null) {317Set<String> knownPackages = JLMA.packages(builder);318if (!allPackages.containsAll(knownPackages)) {319Set<String> missingPackages = new HashSet<>(knownPackages);320missingPackages.removeAll(allPackages);321assert !missingPackages.isEmpty();322String missingPackage = missingPackages.iterator().next();323String tail;324if (usedPackageFinder) {325tail = " not found in module";326} else {327tail = " missing from ModulePackages class file attribute";328}329throw invalidModuleDescriptor("Package " + missingPackage + tail);330331}332builder.packages(allPackages);333}334335ModuleDescriptor descriptor = builder.build();336return new Attributes(descriptor,337moduleTarget,338moduleHashes,339moduleResolution);340}341342/**343* Reads the Module attribute, returning the ModuleDescriptor.Builder to344* build the corresponding ModuleDescriptor.345*/346private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major)347throws IOException348{349// module_name350int module_name_index = in.readUnsignedShort();351String mn = cpool.getModuleName(module_name_index);352353int module_flags = in.readUnsignedShort();354355Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();356boolean open = ((module_flags & ACC_OPEN) != 0);357if (open)358modifiers.add(ModuleDescriptor.Modifier.OPEN);359if ((module_flags & ACC_SYNTHETIC) != 0)360modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);361if ((module_flags & ACC_MANDATED) != 0)362modifiers.add(ModuleDescriptor.Modifier.MANDATED);363364Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);365366int module_version_index = in.readUnsignedShort();367if (module_version_index != 0) {368String vs = cpool.getUtf8(module_version_index);369builder.version(vs);370}371372int requires_count = in.readUnsignedShort();373boolean requiresJavaBase = false;374for (int i=0; i<requires_count; i++) {375int requires_index = in.readUnsignedShort();376String dn = cpool.getModuleName(requires_index);377378int requires_flags = in.readUnsignedShort();379Set<Requires.Modifier> mods;380if (requires_flags == 0) {381mods = Set.of();382} else {383mods = new HashSet<>();384if ((requires_flags & ACC_TRANSITIVE) != 0)385mods.add(Requires.Modifier.TRANSITIVE);386if ((requires_flags & ACC_STATIC_PHASE) != 0)387mods.add(Requires.Modifier.STATIC);388if ((requires_flags & ACC_SYNTHETIC) != 0)389mods.add(Requires.Modifier.SYNTHETIC);390if ((requires_flags & ACC_MANDATED) != 0)391mods.add(Requires.Modifier.MANDATED);392}393394int requires_version_index = in.readUnsignedShort();395if (requires_version_index == 0) {396builder.requires(mods, dn);397} else {398String vs = cpool.getUtf8(requires_version_index);399JLMA.requires(builder, mods, dn, vs);400}401402if (dn.equals("java.base")) {403if (major >= 54404&& (mods.contains(Requires.Modifier.TRANSITIVE)405|| mods.contains(Requires.Modifier.STATIC))) {406String flagName;407if (mods.contains(Requires.Modifier.TRANSITIVE)) {408flagName = "ACC_TRANSITIVE";409} else {410flagName = "ACC_STATIC_PHASE";411}412throw invalidModuleDescriptor("The requires entry for java.base"413+ " has " + flagName + " set");414}415requiresJavaBase = true;416}417}418if (mn.equals("java.base")) {419if (requires_count > 0) {420throw invalidModuleDescriptor("The requires table for java.base"421+ " must be 0 length");422}423} else if (!requiresJavaBase) {424throw invalidModuleDescriptor("The requires table must have"425+ " an entry for java.base");426}427428int exports_count = in.readUnsignedShort();429if (exports_count > 0) {430for (int i=0; i<exports_count; i++) {431int exports_index = in.readUnsignedShort();432String pkg = cpool.getPackageName(exports_index);433434Set<Exports.Modifier> mods;435int exports_flags = in.readUnsignedShort();436if (exports_flags == 0) {437mods = Set.of();438} else {439mods = new HashSet<>();440if ((exports_flags & ACC_SYNTHETIC) != 0)441mods.add(Exports.Modifier.SYNTHETIC);442if ((exports_flags & ACC_MANDATED) != 0)443mods.add(Exports.Modifier.MANDATED);444}445446int exports_to_count = in.readUnsignedShort();447if (exports_to_count > 0) {448Set<String> targets = new HashSet<>(exports_to_count);449for (int j=0; j<exports_to_count; j++) {450int exports_to_index = in.readUnsignedShort();451String target = cpool.getModuleName(exports_to_index);452if (!targets.add(target)) {453throw invalidModuleDescriptor(pkg + " exported to "454+ target + " more than once");455}456}457builder.exports(mods, pkg, targets);458} else {459builder.exports(mods, pkg);460}461}462}463464int opens_count = in.readUnsignedShort();465if (opens_count > 0) {466if (open) {467throw invalidModuleDescriptor("The opens table for an open"468+ " module must be 0 length");469}470for (int i=0; i<opens_count; i++) {471int opens_index = in.readUnsignedShort();472String pkg = cpool.getPackageName(opens_index);473474Set<Opens.Modifier> mods;475int opens_flags = in.readUnsignedShort();476if (opens_flags == 0) {477mods = Set.of();478} else {479mods = new HashSet<>();480if ((opens_flags & ACC_SYNTHETIC) != 0)481mods.add(Opens.Modifier.SYNTHETIC);482if ((opens_flags & ACC_MANDATED) != 0)483mods.add(Opens.Modifier.MANDATED);484}485486int open_to_count = in.readUnsignedShort();487if (open_to_count > 0) {488Set<String> targets = new HashSet<>(open_to_count);489for (int j=0; j<open_to_count; j++) {490int opens_to_index = in.readUnsignedShort();491String target = cpool.getModuleName(opens_to_index);492if (!targets.add(target)) {493throw invalidModuleDescriptor(pkg + " opened to "494+ target + " more than once");495}496}497builder.opens(mods, pkg, targets);498} else {499builder.opens(mods, pkg);500}501}502}503504int uses_count = in.readUnsignedShort();505if (uses_count > 0) {506for (int i=0; i<uses_count; i++) {507int index = in.readUnsignedShort();508String sn = cpool.getClassName(index);509builder.uses(sn);510}511}512513int provides_count = in.readUnsignedShort();514if (provides_count > 0) {515for (int i=0; i<provides_count; i++) {516int index = in.readUnsignedShort();517String sn = cpool.getClassName(index);518int with_count = in.readUnsignedShort();519List<String> providers = new ArrayList<>(with_count);520for (int j=0; j<with_count; j++) {521index = in.readUnsignedShort();522String pn = cpool.getClassName(index);523if (!providers.add(pn)) {524throw invalidModuleDescriptor(sn + " provides " + pn525+ " more than once");526}527}528builder.provides(sn, providers);529}530}531532return builder;533}534535/**536* Reads the ModulePackages attribute537*/538private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)539throws IOException540{541int package_count = in.readUnsignedShort();542Set<String> packages = new HashSet<>(package_count);543for (int i=0; i<package_count; i++) {544int index = in.readUnsignedShort();545String pn = cpool.getPackageName(index);546boolean added = packages.add(pn);547if (!added) {548throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"549+ "attribute more than once");550}551}552return packages;553}554555/**556* Reads the ModuleMainClass attribute557*/558private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)559throws IOException560{561int index = in.readUnsignedShort();562return cpool.getClassName(index);563}564565/**566* Reads the ModuleTarget attribute567*/568private ModuleTarget readModuleTargetAttribute(DataInput in, ConstantPool cpool)569throws IOException570{571String targetPlatform = null;572573int index = in.readUnsignedShort();574if (index != 0)575targetPlatform = cpool.getUtf8(index);576577return new ModuleTarget(targetPlatform);578}579580/**581* Reads the ModuleHashes attribute582*/583private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)584throws IOException585{586int algorithm_index = in.readUnsignedShort();587String algorithm = cpool.getUtf8(algorithm_index);588589int hash_count = in.readUnsignedShort();590Map<String, byte[]> map = new HashMap<>(hash_count);591for (int i=0; i<hash_count; i++) {592int module_name_index = in.readUnsignedShort();593String mn = cpool.getModuleName(module_name_index);594int hash_length = in.readUnsignedShort();595if (hash_length == 0) {596throw invalidModuleDescriptor("hash_length == 0");597}598byte[] hash = new byte[hash_length];599in.readFully(hash);600map.put(mn, hash);601}602603return new ModuleHashes(algorithm, map);604}605606/**607* Reads the ModuleResolution attribute.608*/609private ModuleResolution readModuleResolution(DataInput in,610ConstantPool cpool)611throws IOException612{613int flags = in.readUnsignedShort();614615int reason = 0;616if ((flags & WARN_DEPRECATED) != 0)617reason = WARN_DEPRECATED;618if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0) {619if (reason != 0)620throw invalidModuleDescriptor("Bad module resolution flags:" + flags);621reason = WARN_DEPRECATED_FOR_REMOVAL;622}623if ((flags & WARN_INCUBATING) != 0) {624if (reason != 0)625throw invalidModuleDescriptor("Bad module resolution flags:" + flags);626}627628return new ModuleResolution(flags);629}630631/**632* Returns true if the given attribute can be present at most once633* in the class file. Returns false otherwise.634*/635private static boolean isAttributeAtMostOnce(String name) {636637if (name.equals(MODULE) ||638name.equals(SOURCE_FILE) ||639name.equals(SDE) ||640name.equals(MODULE_PACKAGES) ||641name.equals(MODULE_MAIN_CLASS) ||642name.equals(MODULE_TARGET) ||643name.equals(MODULE_HASHES) ||644name.equals(MODULE_RESOLUTION))645return true;646647return false;648}649650/**651* Return true if the given attribute name is the name of a pre-defined652* attribute in JVMS 4.7 that is not allowed in a module-info class.653*/654private static boolean isAttributeDisallowed(String name) {655Set<String> notAllowed = predefinedNotAllowed;656if (notAllowed == null) {657notAllowed = Set.of(658"ConstantValue",659"Code",660"Deprecated",661"StackMapTable",662"Exceptions",663"EnclosingMethod",664"Signature",665"LineNumberTable",666"LocalVariableTable",667"LocalVariableTypeTable",668"RuntimeVisibleParameterAnnotations",669"RuntimeInvisibleParameterAnnotations",670"RuntimeVisibleTypeAnnotations",671"RuntimeInvisibleTypeAnnotations",672"Synthetic",673"AnnotationDefault",674"BootstrapMethods",675"MethodParameters");676predefinedNotAllowed = notAllowed;677}678return notAllowed.contains(name);679}680681// lazily created set the pre-defined attributes that are not allowed682private static volatile Set<String> predefinedNotAllowed;683684685/**686* The constant pool in a class file.687*/688private static class ConstantPool {689static final int CONSTANT_Utf8 = 1;690static final int CONSTANT_Integer = 3;691static final int CONSTANT_Float = 4;692static final int CONSTANT_Long = 5;693static final int CONSTANT_Double = 6;694static final int CONSTANT_Class = 7;695static final int CONSTANT_String = 8;696static final int CONSTANT_Fieldref = 9;697static final int CONSTANT_Methodref = 10;698static final int CONSTANT_InterfaceMethodref = 11;699static final int CONSTANT_NameAndType = 12;700static final int CONSTANT_MethodHandle = 15;701static final int CONSTANT_MethodType = 16;702static final int CONSTANT_InvokeDynamic = 18;703static final int CONSTANT_Module = 19;704static final int CONSTANT_Package = 20;705706private static class Entry {707protected Entry(int tag) {708this.tag = tag;709}710final int tag;711}712713private static class IndexEntry extends Entry {714IndexEntry(int tag, int index) {715super(tag);716this.index = index;717}718final int index;719}720721private static class Index2Entry extends Entry {722Index2Entry(int tag, int index1, int index2) {723super(tag);724this.index1 = index1;725this.index2 = index2;726}727final int index1, index2;728}729730private static class ValueEntry extends Entry {731ValueEntry(int tag, Object value) {732super(tag);733this.value = value;734}735final Object value;736}737738final Entry[] pool;739740ConstantPool(DataInput in) throws IOException {741int count = in.readUnsignedShort();742pool = new Entry[count];743744for (int i = 1; i < count; i++) {745int tag = in.readUnsignedByte();746switch (tag) {747748case CONSTANT_Utf8:749String svalue = in.readUTF();750pool[i] = new ValueEntry(tag, svalue);751break;752753case CONSTANT_Class:754case CONSTANT_Package:755case CONSTANT_Module:756case CONSTANT_String:757int index = in.readUnsignedShort();758pool[i] = new IndexEntry(tag, index);759break;760761case CONSTANT_Double:762double dvalue = in.readDouble();763pool[i] = new ValueEntry(tag, dvalue);764i++;765break;766767case CONSTANT_Fieldref:768case CONSTANT_InterfaceMethodref:769case CONSTANT_Methodref:770case CONSTANT_InvokeDynamic:771case CONSTANT_NameAndType:772int index1 = in.readUnsignedShort();773int index2 = in.readUnsignedShort();774pool[i] = new Index2Entry(tag, index1, index2);775break;776777case CONSTANT_MethodHandle:778int refKind = in.readUnsignedByte();779index = in.readUnsignedShort();780pool[i] = new Index2Entry(tag, refKind, index);781break;782783case CONSTANT_MethodType:784index = in.readUnsignedShort();785pool[i] = new IndexEntry(tag, index);786break;787788case CONSTANT_Float:789float fvalue = in.readFloat();790pool[i] = new ValueEntry(tag, fvalue);791break;792793case CONSTANT_Integer:794int ivalue = in.readInt();795pool[i] = new ValueEntry(tag, ivalue);796break;797798case CONSTANT_Long:799long lvalue = in.readLong();800pool[i] = new ValueEntry(tag, lvalue);801i++;802break;803804default:805throw invalidModuleDescriptor("Bad constant pool entry: "806+ i);807}808}809}810811String getClassName(int index) {812checkIndex(index);813Entry e = pool[index];814if (e.tag != CONSTANT_Class) {815throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "816+ index);817}818String value = getUtf8(((IndexEntry) e).index);819checkUnqualifiedName("CONSTANT_Class", index, value);820return value.replace('/', '.'); // internal form -> binary name821}822823String getPackageName(int index) {824checkIndex(index);825Entry e = pool[index];826if (e.tag != CONSTANT_Package) {827throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "828+ index);829}830String value = getUtf8(((IndexEntry) e).index);831checkUnqualifiedName("CONSTANT_Package", index, value);832return value.replace('/', '.'); // internal form -> binary name833}834835String getModuleName(int index) {836checkIndex(index);837Entry e = pool[index];838if (e.tag != CONSTANT_Module) {839throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "840+ index);841}842String value = getUtf8(((IndexEntry) e).index);843return decodeModuleName(index, value);844}845846String getUtf8(int index) {847checkIndex(index);848Entry e = pool[index];849if (e.tag != CONSTANT_Utf8) {850throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "851+ index);852}853return (String) (((ValueEntry) e).value);854}855856void checkIndex(int index) {857if (index < 1 || index >= pool.length)858throw invalidModuleDescriptor("Index into constant pool out of range");859}860861void checkUnqualifiedName(String what, int index, String value) {862int len = value.length();863if (len == 0) {864throw invalidModuleDescriptor(what + " at entry " + index865+ " has zero length");866}867for (int i=0; i<len; i++) {868char c = value.charAt(i);869if (c == '.' || c == ';' || c == '[') {870throw invalidModuleDescriptor(what + " at entry " + index871+ " has illegal character: '"872+ c + "'");873}874}875}876877/**878* "Decode" a module name that has been read from the constant pool.879*/880String decodeModuleName(int index, String value) {881int len = value.length();882if (len == 0) {883throw invalidModuleDescriptor("CONSTANT_Module at entry "884+ index + " is zero length");885}886int i = 0;887while (i < len) {888int cp = value.codePointAt(i);889if (cp == ':' || cp == '@' || cp < 0x20) {890throw invalidModuleDescriptor("CONSTANT_Module at entry "891+ index + " has illegal character: "892+ Character.getName(cp));893}894895// blackslash is the escape character896if (cp == '\\')897return decodeModuleName(index, i, value);898899i += Character.charCount(cp);900}901return value;902}903904/**905* "Decode" a module name that has been read from the constant pool and906* partly checked for illegal characters (up to position {@code i}).907*/908String decodeModuleName(int index, int i, String value) {909StringBuilder sb = new StringBuilder();910911// copy the code points that have been checked912int j = 0;913while (j < i) {914int cp = value.codePointAt(j);915sb.appendCodePoint(cp);916j += Character.charCount(cp);917}918919// decode from position {@code i} to end920int len = value.length();921while (i < len) {922int cp = value.codePointAt(i);923if (cp == ':' || cp == '@' || cp < 0x20) {924throw invalidModuleDescriptor("CONSTANT_Module at entry "925+ index + " has illegal character: "926+ Character.getName(cp));927}928929// blackslash is the escape character930if (cp == '\\') {931j = i + Character.charCount(cp);932if (j >= len) {933throw invalidModuleDescriptor("CONSTANT_Module at entry "934+ index + " has illegal "935+ "escape sequence");936}937int next = value.codePointAt(j);938if (next != '\\' && next != ':' && next != '@') {939throw invalidModuleDescriptor("CONSTANT_Module at entry "940+ index + " has illegal "941+ "escape sequence");942}943sb.appendCodePoint(next);944i += Character.charCount(next);945} else {946sb.appendCodePoint(cp);947}948949i += Character.charCount(cp);950}951return sb.toString();952}953}954955/**956* A DataInput implementation that reads from a ByteBuffer.957*/958private static class DataInputWrapper implements DataInput {959private final ByteBuffer bb;960961DataInputWrapper(ByteBuffer bb) {962this.bb = bb;963}964965@Override966public void readFully(byte b[]) throws IOException {967readFully(b, 0, b.length);968}969970@Override971public void readFully(byte b[], int off, int len) throws IOException {972try {973bb.get(b, off, len);974} catch (BufferUnderflowException e) {975throw new EOFException(e.getMessage());976}977}978979@Override980public int skipBytes(int n) {981int skip = Math.min(n, bb.remaining());982bb.position(bb.position() + skip);983return skip;984}985986@Override987public boolean readBoolean() throws IOException {988try {989int ch = bb.get();990return (ch != 0);991} catch (BufferUnderflowException e) {992throw new EOFException(e.getMessage());993}994}995996@Override997public byte readByte() throws IOException {998try {999return bb.get();1000} catch (BufferUnderflowException e) {1001throw new EOFException(e.getMessage());1002}1003}10041005@Override1006public int readUnsignedByte() throws IOException {1007try {1008return ((int) bb.get()) & 0xff;1009} catch (BufferUnderflowException e) {1010throw new EOFException(e.getMessage());1011}1012}10131014@Override1015public short readShort() throws IOException {1016try {1017return bb.getShort();1018} catch (BufferUnderflowException e) {1019throw new EOFException(e.getMessage());1020}1021}10221023@Override1024public int readUnsignedShort() throws IOException {1025try {1026return ((int) bb.getShort()) & 0xffff;1027} catch (BufferUnderflowException e) {1028throw new EOFException(e.getMessage());1029}1030}10311032@Override1033public char readChar() throws IOException {1034try {1035return bb.getChar();1036} catch (BufferUnderflowException e) {1037throw new EOFException(e.getMessage());1038}1039}10401041@Override1042public int readInt() throws IOException {1043try {1044return bb.getInt();1045} catch (BufferUnderflowException e) {1046throw new EOFException(e.getMessage());1047}1048}10491050@Override1051public long readLong() throws IOException {1052try {1053return bb.getLong();1054} catch (BufferUnderflowException e) {1055throw new EOFException(e.getMessage());1056}1057}10581059@Override1060public float readFloat() throws IOException {1061try {1062return bb.getFloat();1063} catch (BufferUnderflowException e) {1064throw new EOFException(e.getMessage());1065}1066}10671068@Override1069public double readDouble() throws IOException {1070try {1071return bb.getDouble();1072} catch (BufferUnderflowException e) {1073throw new EOFException(e.getMessage());1074}1075}10761077@Override1078public String readLine() {1079throw new RuntimeException("not implemented");1080}10811082@Override1083public String readUTF() throws IOException {1084// ### Need to measure the performance and feasibility of using1085// the UTF-8 decoder instead.1086return DataInputStream.readUTF(this);1087}1088}10891090/**1091* A DataInput implementation that reads from another DataInput and counts1092* the number of bytes read.1093*/1094private static class CountingDataInput implements DataInput {1095private final DataInput delegate;1096private long count;10971098CountingDataInput(DataInput delegate) {1099this.delegate = delegate;1100}11011102long count() {1103return count;1104}11051106@Override1107public void readFully(byte b[]) throws IOException {1108delegate.readFully(b, 0, b.length);1109count += b.length;1110}11111112@Override1113public void readFully(byte b[], int off, int len) throws IOException {1114delegate.readFully(b, off, len);1115count += len;1116}11171118@Override1119public int skipBytes(int n) throws IOException {1120int skip = delegate.skipBytes(n);1121count += skip;1122return skip;1123}11241125@Override1126public boolean readBoolean() throws IOException {1127boolean b = delegate.readBoolean();1128count++;1129return b;1130}11311132@Override1133public byte readByte() throws IOException {1134byte b = delegate.readByte();1135count++;1136return b;1137}11381139@Override1140public int readUnsignedByte() throws IOException {1141int i = delegate.readUnsignedByte();1142count++;1143return i;1144}11451146@Override1147public short readShort() throws IOException {1148short s = delegate.readShort();1149count += 2;1150return s;1151}11521153@Override1154public int readUnsignedShort() throws IOException {1155int s = delegate.readUnsignedShort();1156count += 2;1157return s;1158}11591160@Override1161public char readChar() throws IOException {1162char c = delegate.readChar();1163count += 2;1164return c;1165}11661167@Override1168public int readInt() throws IOException {1169int i = delegate.readInt();1170count += 4;1171return i;1172}11731174@Override1175public long readLong() throws IOException {1176long l = delegate.readLong();1177count += 8;1178return l;1179}11801181@Override1182public float readFloat() throws IOException {1183float f = delegate.readFloat();1184count += 4;1185return f;1186}11871188@Override1189public double readDouble() throws IOException {1190double d = delegate.readDouble();1191count += 8;1192return d;1193}11941195@Override1196public String readLine() {1197throw new RuntimeException("not implemented");1198}11991200@Override1201public String readUTF() throws IOException {1202return DataInputStream.readUTF(this);1203}1204}12051206/**1207* Returns an InvalidModuleDescriptorException with the given detail1208* message1209*/1210private static InvalidModuleDescriptorException invalidModuleDescriptor(String msg) {1211return new InvalidModuleDescriptorException(msg);1212}12131214/**1215* Returns an InvalidModuleDescriptorException with a detail message to1216* indicate that the class file is truncated.1217*/1218private static InvalidModuleDescriptorException truncatedModuleDescriptor() {1219return invalidModuleDescriptor("Truncated module-info.class");1220}12211222}122312241225