/* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.jvm; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.nio.CharBuffer; import java.nio.file.ClosedFileSystemException; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.comp.Annotate; import com.sun.tools.javac.comp.Annotate.AnnotationTypeCompleter; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.*; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata; import com.sun.tools.javac.file.BaseFileManager; import com.sun.tools.javac.file.PathFileObject; import com.sun.tools.javac.jvm.ClassFile.NameAndType; import com.sun.tools.javac.jvm.ClassFile.Version; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; import com.sun.tools.javac.code.Scope.LookupKind; import static com.sun.tools.javac.code.TypeTag.ARRAY; import static com.sun.tools.javac.code.TypeTag.CLASS; import static com.sun.tools.javac.code.TypeTag.TYPEVAR; import static com.sun.tools.javac.jvm.ClassFile.*; import static com.sun.tools.javac.jvm.ClassFile.Version.*; import static com.sun.tools.javac.main.Option.PARAMETERS; /** This class provides operations to read a classfile into an internal * representation. The internal representation is anchored in a * ClassSymbol which contains in its scope symbol representations * for all other definitions in the classfile. Top-level Classes themselves * appear as members of the scopes of PackageSymbols. * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice. */ public class ClassReader { /** The context key for the class reader. */ protected static final Context.Key classReaderKey = new Context.Key<>(); public static final int INITIAL_BUFFER_SIZE = 0x0fff0; private final Annotate annotate; /** Switch: verbose output. */ boolean verbose; /** Switch: read constant pool and code sections. This switch is initially * set to false but can be turned on from outside. */ public boolean readAllOfClassFile = false; /** Switch: allow simplified varargs. */ boolean allowSimplifiedVarargs; /** Switch: allow modules. */ boolean allowModules; /** Lint option: warn about classfile issues */ boolean lintClassfile; /** Switch: preserve parameter names from the variable table. */ public boolean saveParameterNames; /** * The currently selected profile. */ public final Profile profile; /** The log to use for verbose output */ final Log log; /** The symbol table. */ Symtab syms; Types types; /** The name table. */ final Names names; /** Access to files */ private final JavaFileManager fileManager; /** Factory for diagnostics */ JCDiagnostic.Factory diagFactory; DeferredCompletionFailureHandler dcfh; /** The current scope where type variables are entered. */ protected WriteableScope typevars; private List interimUses = List.nil(); private List interimProvides = List.nil(); /** The path name of the class file currently being read. */ protected JavaFileObject currentClassFile = null; /** The class or method currently being read. */ protected Symbol currentOwner = null; /** The module containing the class currently being read. */ protected ModuleSymbol currentModule = null; /** The buffer containing the currently read class file. */ byte[] buf = new byte[INITIAL_BUFFER_SIZE]; /** The current input pointer. */ protected int bp; /** The objects of the constant pool. */ Object[] poolObj; /** For every constant pool entry, an index into buf where the * defining section of the entry is found. */ int[] poolIdx; /** The major version number of the class file being read. */ int majorVersion; /** The minor version number of the class file being read. */ int minorVersion; /** A table to hold the constant pool indices for method parameter * names, as given in LocalVariableTable attributes. */ int[] parameterNameIndices; /** * A table to hold annotations for method parameters. */ ParameterAnnotations[] parameterAnnotations; /** * A holder for parameter annotations. */ static class ParameterAnnotations { List proxies; void add(List newAnnotations) { if (proxies == null) { proxies = newAnnotations; } else { proxies = proxies.prependList(newAnnotations); } } } /** * Whether or not any parameter names have been found. */ boolean haveParameterNameIndices; /** Set this to false every time we start reading a method * and are saving parameter names. Set it to true when we see * MethodParameters, if it's set when we see a LocalVariableTable, * then we ignore the parameter names from the LVT. */ boolean sawMethodParameters; /** * The set of attribute names for which warnings have been generated for the current class */ Set warnedAttrs = new HashSet<>(); /** * The prototype @Target Attribute.Compound if this class is an annotation annotated with * @Target */ CompoundAnnotationProxy target; /** * The prototype @Repetable Attribute.Compound if this class is an annotation annotated with * @Repeatable */ CompoundAnnotationProxy repeatable; /** Get the ClassReader instance for this invocation. */ public static ClassReader instance(Context context) { ClassReader instance = context.get(classReaderKey); if (instance == null) instance = new ClassReader(context); return instance; } /** Construct a new class reader. */ protected ClassReader(Context context) { context.put(classReaderKey, this); annotate = Annotate.instance(context); names = Names.instance(context); syms = Symtab.instance(context); types = Types.instance(context); fileManager = context.get(JavaFileManager.class); if (fileManager == null) throw new AssertionError("FileManager initialization error"); diagFactory = JCDiagnostic.Factory.instance(context); dcfh = DeferredCompletionFailureHandler.instance(context); log = Log.instance(context); Options options = Options.instance(context); verbose = options.isSet(Option.VERBOSE); Source source = Source.instance(context); allowSimplifiedVarargs = Feature.SIMPLIFIED_VARARGS.allowedInSource(source); allowModules = Feature.MODULES.allowedInSource(source); saveParameterNames = options.isSet(PARAMETERS); profile = Profile.instance(context); typevars = WriteableScope.create(syms.noSymbol); lintClassfile = Lint.instance(context).isEnabled(LintCategory.CLASSFILE); initAttributeReaders(); } /** Add member to class unless it is synthetic. */ private void enterMember(ClassSymbol c, Symbol sym) { // Synthetic members are not entered -- reason lost to history (optimization?). // Lambda methods must be entered because they may have inner classes (which reference them) if ((sym.flags_field & (SYNTHETIC|BRIDGE)) != SYNTHETIC || sym.name.startsWith(names.lambda)) c.members_field.enter(sym); } /************************************************************************ * Error Diagnoses ***********************************************************************/ public ClassFinder.BadClassFile badClassFile(String key, Object... args) { return new ClassFinder.BadClassFile ( currentOwner.enclClass(), currentClassFile, diagFactory.fragment(key, args), diagFactory, dcfh); } public ClassFinder.BadEnclosingMethodAttr badEnclosingMethod(Symbol sym) { return new ClassFinder.BadEnclosingMethodAttr ( currentOwner.enclClass(), currentClassFile, diagFactory.fragment(Fragments.BadEnclosingMethod(sym)), diagFactory, dcfh); } /************************************************************************ * Buffer Access ***********************************************************************/ /** Read a character. */ char nextChar() { return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF)); } /** Read a byte. */ int nextByte() { return buf[bp++] & 0xFF; } /** Read an integer. */ int nextInt() { return ((buf[bp++] & 0xFF) << 24) + ((buf[bp++] & 0xFF) << 16) + ((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF); } /** Extract a character at position bp from buf. */ char getChar(int bp) { return (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF)); } /** Extract an integer at position bp from buf. */ int getInt(int bp) { return ((buf[bp] & 0xFF) << 24) + ((buf[bp+1] & 0xFF) << 16) + ((buf[bp+2] & 0xFF) << 8) + (buf[bp+3] & 0xFF); } /** Extract a long integer at position bp from buf. */ long getLong(int bp) { DataInputStream bufin = new DataInputStream(new ByteArrayInputStream(buf, bp, 8)); try { return bufin.readLong(); } catch (IOException e) { throw new AssertionError(e); } } /** Extract a float at position bp from buf. */ float getFloat(int bp) { DataInputStream bufin = new DataInputStream(new ByteArrayInputStream(buf, bp, 4)); try { return bufin.readFloat(); } catch (IOException e) { throw new AssertionError(e); } } /** Extract a double at position bp from buf. */ double getDouble(int bp) { DataInputStream bufin = new DataInputStream(new ByteArrayInputStream(buf, bp, 8)); try { return bufin.readDouble(); } catch (IOException e) { throw new AssertionError(e); } } /************************************************************************ * Constant Pool Access ***********************************************************************/ /** Index all constant pool entries, writing their start addresses into * poolIdx. */ void indexPool() { poolIdx = new int[nextChar()]; poolObj = new Object[poolIdx.length]; int i = 1; while (i < poolIdx.length) { poolIdx[i++] = bp; byte tag = buf[bp++]; switch (tag) { case CONSTANT_Utf8: case CONSTANT_Unicode: { int len = nextChar(); bp = bp + len; break; } case CONSTANT_Class: case CONSTANT_String: case CONSTANT_MethodType: case CONSTANT_Module: case CONSTANT_Package: bp = bp + 2; break; case CONSTANT_MethodHandle: bp = bp + 3; break; case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: case CONSTANT_NameandType: case CONSTANT_Integer: case CONSTANT_Float: case CONSTANT_Dynamic: case CONSTANT_InvokeDynamic: bp = bp + 4; break; case CONSTANT_Long: case CONSTANT_Double: bp = bp + 8; i++; break; default: throw badClassFile("bad.const.pool.tag.at", Byte.toString(tag), Integer.toString(bp -1)); } } } /** Read constant pool entry at start address i, use pool as a cache. */ Object readPool(int i) { Object result = poolObj[i]; if (result != null) return result; int index = poolIdx[i]; if (index == 0) return null; byte tag = buf[index]; switch (tag) { case CONSTANT_Utf8: poolObj[i] = names.fromUtf(buf, index + 3, getChar(index + 1)); break; case CONSTANT_Unicode: throw badClassFile("unicode.str.not.supported"); case CONSTANT_Class: poolObj[i] = readClassOrType(getChar(index + 1)); break; case CONSTANT_String: // FIXME: (footprint) do not use toString here poolObj[i] = readName(getChar(index + 1)).toString(); break; case CONSTANT_Fieldref: { ClassSymbol owner = readClassSymbol(getChar(index + 1)); NameAndType nt = readNameAndType(getChar(index + 3)); poolObj[i] = new VarSymbol(0, nt.name, nt.uniqueType.type, owner); break; } case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: { ClassSymbol owner = readClassSymbol(getChar(index + 1)); NameAndType nt = readNameAndType(getChar(index + 3)); poolObj[i] = new MethodSymbol(0, nt.name, nt.uniqueType.type, owner); break; } case CONSTANT_NameandType: poolObj[i] = new NameAndType( readName(getChar(index + 1)), readType(getChar(index + 3)), types); break; case CONSTANT_Integer: poolObj[i] = getInt(index + 1); break; case CONSTANT_Float: poolObj[i] = Float.valueOf(getFloat(index + 1)); break; case CONSTANT_Long: poolObj[i] = Long.valueOf(getLong(index + 1)); break; case CONSTANT_Double: poolObj[i] = Double.valueOf(getDouble(index + 1)); break; case CONSTANT_MethodHandle: skipBytes(4); break; case CONSTANT_MethodType: skipBytes(3); break; case CONSTANT_Dynamic: case CONSTANT_InvokeDynamic: skipBytes(5); break; case CONSTANT_Module: case CONSTANT_Package: // this is temporary for now: treat as a simple reference to the underlying Utf8. poolObj[i] = readName(getChar(index + 1)); break; default: throw badClassFile("bad.const.pool.tag", Byte.toString(tag)); } return poolObj[i]; } /** Read signature and convert to type. */ Type readType(int i) { int index = poolIdx[i]; return sigToType(buf, index + 3, getChar(index + 1)); } /** If name is an array type or class signature, return the * corresponding type; otherwise return a ClassSymbol with given name. */ Object readClassOrType(int i) { int index = poolIdx[i]; int len = getChar(index + 1); int start = index + 3; Assert.check(buf[start] == '[' || buf[start + len - 1] != ';'); // by the above assertion, the following test can be // simplified to (buf[start] == '[') return (buf[start] == '[' || buf[start + len - 1] == ';') ? (Object)sigToType(buf, start, len) : (Object)enterClass(names.fromUtf(internalize(buf, start, len))); } /** Read signature and convert to type parameters. */ List readTypeParams(int i) { int index = poolIdx[i]; return sigToTypeParams(buf, index + 3, getChar(index + 1)); } /** Read class entry. */ ClassSymbol readClassSymbol(int i) { Object obj = readPool(i); if (obj != null && !(obj instanceof ClassSymbol)) throw badClassFile("bad.const.pool.entry", currentClassFile.toString(), "CONSTANT_Class_info", i); return (ClassSymbol)obj; } Name readClassName(int i) { int index = poolIdx[i]; if (index == 0) return null; byte tag = buf[index]; if (tag != CONSTANT_Class) { throw badClassFile("bad.const.pool.entry", currentClassFile.toString(), "CONSTANT_Class_info", i); } int nameIndex = poolIdx[getChar(index + 1)]; int len = getChar(nameIndex + 1); int start = nameIndex + 3; if (buf[start] == '[' || buf[start + len - 1] == ';') throw badClassFile("wrong class name"); //TODO: proper diagnostics return names.fromUtf(internalize(buf, start, len)); } /** Read name. */ Name readName(int i) { Object obj = readPool(i); if (obj != null && !(obj instanceof Name)) throw badClassFile("bad.const.pool.entry", currentClassFile.toString(), "CONSTANT_Utf8_info or CONSTANT_String_info", i); return (Name)obj; } /** Read name and type. */ NameAndType readNameAndType(int i) { Object obj = readPool(i); if (obj != null && !(obj instanceof NameAndType)) throw badClassFile("bad.const.pool.entry", currentClassFile.toString(), "CONSTANT_NameAndType_info", i); return (NameAndType)obj; } /** Read the name of a module. * The name is stored in a CONSTANT_Module entry, in * JVMS 4.2 binary form (using ".", not "/") */ Name readModuleName(int i) { return readName(i); } /** Read module_flags. */ Set readModuleFlags(int flags) { Set set = EnumSet.noneOf(ModuleFlags.class); for (ModuleFlags f : ModuleFlags.values()) { if ((flags & f.value) != 0) set.add(f); } return set; } /** Read resolution_flags. */ Set readModuleResolutionFlags(int flags) { Set set = EnumSet.noneOf(ModuleResolutionFlags.class); for (ModuleResolutionFlags f : ModuleResolutionFlags.values()) { if ((flags & f.value) != 0) set.add(f); } return set; } /** Read exports_flags. */ Set readExportsFlags(int flags) { Set set = EnumSet.noneOf(ExportsFlag.class); for (ExportsFlag f: ExportsFlag.values()) { if ((flags & f.value) != 0) set.add(f); } return set; } /** Read opens_flags. */ Set readOpensFlags(int flags) { Set set = EnumSet.noneOf(OpensFlag.class); for (OpensFlag f: OpensFlag.values()) { if ((flags & f.value) != 0) set.add(f); } return set; } /** Read requires_flags. */ Set readRequiresFlags(int flags) { Set set = EnumSet.noneOf(RequiresFlag.class); for (RequiresFlag f: RequiresFlag.values()) { if ((flags & f.value) != 0) set.add(f); } return set; } /************************************************************************ * Reading Types ***********************************************************************/ /** The unread portion of the currently read type is * signature[sigp..siglimit-1]. */ byte[] signature; int sigp; int siglimit; boolean sigEnterPhase = false; /** Convert signature to type, where signature is a byte array segment. */ Type sigToType(byte[] sig, int offset, int len) { signature = sig; sigp = offset; siglimit = offset + len; return sigToType(); } /** Convert signature to type, where signature is implicit. */ Type sigToType() { switch ((char) signature[sigp]) { case 'T': sigp++; int start = sigp; while (signature[sigp] != ';') sigp++; sigp++; return sigEnterPhase ? Type.noType : findTypeVar(names.fromUtf(signature, start, sigp - 1 - start)); case '+': { sigp++; Type t = sigToType(); return new WildcardType(t, BoundKind.EXTENDS, syms.boundClass); } case '*': sigp++; return new WildcardType(syms.objectType, BoundKind.UNBOUND, syms.boundClass); case '-': { sigp++; Type t = sigToType(); return new WildcardType(t, BoundKind.SUPER, syms.boundClass); } case 'B': sigp++; return syms.byteType; case 'C': sigp++; return syms.charType; case 'D': sigp++; return syms.doubleType; case 'F': sigp++; return syms.floatType; case 'I': sigp++; return syms.intType; case 'J': sigp++; return syms.longType; case 'L': { // int oldsigp = sigp; Type t = classSigToType(); if (sigp < siglimit && signature[sigp] == '.') throw badClassFile("deprecated inner class signature syntax " + "(please recompile from source)"); /* System.err.println(" decoded " + new String(signature, oldsigp, sigp-oldsigp) + " => " + t + " outer " + t.outer()); */ return t; } case 'S': sigp++; return syms.shortType; case 'V': sigp++; return syms.voidType; case 'Z': sigp++; return syms.booleanType; case '[': sigp++; return new ArrayType(sigToType(), syms.arrayClass); case '(': sigp++; List argtypes = sigToTypes(')'); Type restype = sigToType(); List thrown = List.nil(); while (signature[sigp] == '^') { sigp++; thrown = thrown.prepend(sigToType()); } // if there is a typevar in the throws clause we should state it. for (List l = thrown; l.nonEmpty(); l = l.tail) { if (l.head.hasTag(TYPEVAR)) { l.head.tsym.flags_field |= THROWS; } } return new MethodType(argtypes, restype, thrown.reverse(), syms.methodClass); case '<': typevars = typevars.dup(currentOwner); Type poly = new ForAll(sigToTypeParams(), sigToType()); typevars = typevars.leave(); return poly; default: throw badClassFile("bad.signature", Convert.utf2string(signature, sigp, 10)); } } byte[] signatureBuffer = new byte[0]; int sbp = 0; /** Convert class signature to type, where signature is implicit. */ Type classSigToType() { if (signature[sigp] != 'L') throw badClassFile("bad.class.signature", Convert.utf2string(signature, sigp, 10)); sigp++; Type outer = Type.noType; int startSbp = sbp; while (true) { final byte c = signature[sigp++]; switch (c) { case ';': { // end ClassSymbol t = enterClass(names.fromUtf(signatureBuffer, startSbp, sbp - startSbp)); try { return (outer == Type.noType) ? t.erasure(types) : new ClassType(outer, List.nil(), t); } finally { sbp = startSbp; } } case '<': // generic arguments ClassSymbol t = enterClass(names.fromUtf(signatureBuffer, startSbp, sbp - startSbp)); outer = new ClassType(outer, sigToTypes('>'), t) { boolean completed = false; @Override @DefinedBy(Api.LANGUAGE_MODEL) public Type getEnclosingType() { if (!completed) { completed = true; tsym.complete(); Type enclosingType = tsym.type.getEnclosingType(); if (enclosingType != Type.noType) { List typeArgs = super.getEnclosingType().allparams(); List typeParams = enclosingType.allparams(); if (typeParams.length() != typeArgs.length()) { // no "rare" types super.setEnclosingType(types.erasure(enclosingType)); } else { super.setEnclosingType(types.subst(enclosingType, typeParams, typeArgs)); } } else { super.setEnclosingType(Type.noType); } } return super.getEnclosingType(); } @Override public void setEnclosingType(Type outer) { throw new UnsupportedOperationException(); } }; switch (signature[sigp++]) { case ';': if (sigp < signature.length && signature[sigp] == '.') { // support old-style GJC signatures // The signature produced was // Lfoo/Outer;.Lfoo/Outer$Inner; // rather than say // Lfoo/Outer.Inner; // so we skip past ".Lfoo/Outer$" sigp += (sbp - startSbp) + // "foo/Outer" 3; // ".L" and "$" signatureBuffer[sbp++] = (byte)'$'; break; } else { sbp = startSbp; return outer; } case '.': signatureBuffer[sbp++] = (byte)'$'; break; default: throw new AssertionError(signature[sigp-1]); } continue; case '.': //we have seen an enclosing non-generic class if (outer != Type.noType) { t = enterClass(names.fromUtf(signatureBuffer, startSbp, sbp - startSbp)); outer = new ClassType(outer, List.nil(), t); } signatureBuffer[sbp++] = (byte)'$'; continue; case '/': signatureBuffer[sbp++] = (byte)'.'; continue; default: signatureBuffer[sbp++] = c; continue; } } } /** Convert (implicit) signature to list of types * until `terminator' is encountered. */ List sigToTypes(char terminator) { List head = List.of(null); List tail = head; while (signature[sigp] != terminator) tail = tail.setTail(List.of(sigToType())); sigp++; return head.tail; } /** Convert signature to type parameters, where signature is a byte * array segment. */ List sigToTypeParams(byte[] sig, int offset, int len) { signature = sig; sigp = offset; siglimit = offset + len; return sigToTypeParams(); } /** Convert signature to type parameters, where signature is implicit. */ List sigToTypeParams() { List tvars = List.nil(); if (signature[sigp] == '<') { sigp++; int start = sigp; sigEnterPhase = true; while (signature[sigp] != '>') tvars = tvars.prepend(sigToTypeParam()); sigEnterPhase = false; sigp = start; while (signature[sigp] != '>') sigToTypeParam(); sigp++; } return tvars.reverse(); } /** Convert (implicit) signature to type parameter. */ Type sigToTypeParam() { int start = sigp; while (signature[sigp] != ':') sigp++; Name name = names.fromUtf(signature, start, sigp - start); TypeVar tvar; if (sigEnterPhase) { tvar = new TypeVar(name, currentOwner, syms.botType); typevars.enter(tvar.tsym); } else { tvar = (TypeVar)findTypeVar(name); } List bounds = List.nil(); boolean allInterfaces = false; if (signature[sigp] == ':' && signature[sigp+1] == ':') { sigp++; allInterfaces = true; } while (signature[sigp] == ':') { sigp++; bounds = bounds.prepend(sigToType()); } if (!sigEnterPhase) { types.setBounds(tvar, bounds.reverse(), allInterfaces); } return tvar; } /** Find type variable with given name in `typevars' scope. */ Type findTypeVar(Name name) { Symbol s = typevars.findFirst(name); if (s != null) { return s.type; } else { if (readingClassAttr) { // While reading the class attribute, the supertypes // might refer to a type variable from an enclosing element // (method or class). // If the type variable is defined in the enclosing class, // we can actually find it in // currentOwner.owner.type.getTypeArguments() // However, until we have read the enclosing method attribute // we don't know for sure if this owner is correct. It could // be a method and there is no way to tell before reading the // enclosing method attribute. TypeVar t = new TypeVar(name, currentOwner, syms.botType); missingTypeVariables = missingTypeVariables.prepend(t); // System.err.println("Missing type var " + name); return t; } throw badClassFile("undecl.type.var", name); } } /************************************************************************ * Reading Attributes ***********************************************************************/ protected enum AttributeKind { CLASS, MEMBER } protected abstract class AttributeReader { protected AttributeReader(Name name, ClassFile.Version version, Set kinds) { this.name = name; this.version = version; this.kinds = kinds; } protected boolean accepts(AttributeKind kind) { if (kinds.contains(kind)) { if (majorVersion > version.major || (majorVersion == version.major && minorVersion >= version.minor)) return true; if (lintClassfile && !warnedAttrs.contains(name)) { JavaFileObject prev = log.useSource(currentClassFile); try { log.warning(LintCategory.CLASSFILE, (DiagnosticPosition) null, Warnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); } finally { log.useSource(prev); } warnedAttrs.add(name); } } return false; } protected abstract void read(Symbol sym, int attrLen); protected final Name name; protected final ClassFile.Version version; protected final Set kinds; } protected Set CLASS_ATTRIBUTE = EnumSet.of(AttributeKind.CLASS); protected Set MEMBER_ATTRIBUTE = EnumSet.of(AttributeKind.MEMBER); protected Set CLASS_OR_MEMBER_ATTRIBUTE = EnumSet.of(AttributeKind.CLASS, AttributeKind.MEMBER); protected Map attributeReaders = new HashMap<>(); private void initAttributeReaders() { AttributeReader[] readers = { // v45.3 attributes new AttributeReader(names.Code, V45_3, MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { if (readAllOfClassFile || saveParameterNames) ((MethodSymbol)sym).code = readCode(sym); else bp = bp + attrLen; } }, new AttributeReader(names.ConstantValue, V45_3, MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { Object v = readPool(nextChar()); // Ignore ConstantValue attribute if field not final. if ((sym.flags() & FINAL) == 0) { return; } VarSymbol var = (VarSymbol) sym; switch (var.type.getTag()) { case BOOLEAN: case BYTE: case CHAR: case SHORT: case INT: checkType(var, Integer.class, v); break; case LONG: checkType(var, Long.class, v); break; case FLOAT: checkType(var, Float.class, v); break; case DOUBLE: checkType(var, Double.class, v); break; case CLASS: Assert.check(var.type.tsym == syms.stringType.tsym); checkType(var, String.class, v); break; default: // ignore ConstantValue attribute if type is not primitive or String return; } if (v instanceof Integer && !var.type.getTag().checkRange((Integer) v)) { throw badClassFile("bad.constant.range", v, var, var.type); } var.setData(v); } void checkType(Symbol var, Class clazz, Object value) { if (!clazz.isInstance(value)) { throw badClassFile("bad.constant.value", value, var, clazz.getSimpleName()); } } }, new AttributeReader(names.Deprecated, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { Symbol s = sym.owner.kind == MDL ? sym.owner : sym; s.flags_field |= DEPRECATED; } }, new AttributeReader(names.Exceptions, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { int nexceptions = nextChar(); List thrown = List.nil(); for (int j = 0; j < nexceptions; j++) thrown = thrown.prepend(readClassSymbol(nextChar()).type); if (sym.type.getThrownTypes().isEmpty()) sym.type.asMethodType().thrown = thrown.reverse(); } }, new AttributeReader(names.InnerClasses, V45_3, CLASS_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { ClassSymbol c = (ClassSymbol) sym; if (currentModule.module_info == c) { //prevent entering the classes too soon: skipInnerClasses(); } else { readInnerClasses(c); } } }, new AttributeReader(names.LocalVariableTable, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { int newbp = bp + attrLen; if (saveParameterNames && !sawMethodParameters) { // Pick up parameter names from the variable table. // Parameter names are not explicitly identified as such, // but all parameter name entries in the LocalVariableTable // have a start_pc of 0. Therefore, we record the name // indicies of all slots with a start_pc of zero in the // parameterNameIndicies array. // Note that this implicitly honors the JVMS spec that // there may be more than one LocalVariableTable, and that // there is no specified ordering for the entries. int numEntries = nextChar(); for (int i = 0; i < numEntries; i++) { int start_pc = nextChar(); int length = nextChar(); int nameIndex = nextChar(); int sigIndex = nextChar(); int register = nextChar(); if (start_pc == 0) { // ensure array large enough if (register >= parameterNameIndices.length) { int newSize = Math.max(register + 1, parameterNameIndices.length + 8); parameterNameIndices = Arrays.copyOf(parameterNameIndices, newSize); } parameterNameIndices[register] = nameIndex; haveParameterNameIndices = true; } } } bp = newbp; } }, new AttributeReader(names.SourceFile, V45_3, CLASS_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { ClassSymbol c = (ClassSymbol) sym; Name n = readName(nextChar()); c.sourcefile = new SourceFileObject(n, c.flatname); // If the class is a toplevel class, originating from a Java source file, // but the class name does not match the file name, then it is // an auxiliary class. String sn = n.toString(); if (c.owner.kind == PCK && sn.endsWith(".java") && !sn.equals(c.name.toString()+".java")) { c.flags_field |= AUXILIARY; } } }, new AttributeReader(names.Synthetic, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { sym.flags_field |= SYNTHETIC; } }, // standard v49 attributes new AttributeReader(names.EnclosingMethod, V49, CLASS_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { int newbp = bp + attrLen; readEnclosingMethodAttr(sym); bp = newbp; } }, new AttributeReader(names.Signature, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { if (sym.kind == TYP) { ClassSymbol c = (ClassSymbol) sym; readingClassAttr = true; try { ClassType ct1 = (ClassType)c.type; Assert.check(c == currentOwner); ct1.typarams_field = readTypeParams(nextChar()); ct1.supertype_field = sigToType(); ListBuffer is = new ListBuffer<>(); while (sigp != siglimit) is.append(sigToType()); ct1.interfaces_field = is.toList(); } finally { readingClassAttr = false; } } else { List thrown = sym.type.getThrownTypes(); sym.type = readType(nextChar()); //- System.err.println(" # " + sym.type); if (sym.kind == MTH && sym.type.getThrownTypes().isEmpty()) sym.type.asMethodType().thrown = thrown; } } }, // v49 annotation attributes new AttributeReader(names.AnnotationDefault, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { attachAnnotationDefault(sym); } }, new AttributeReader(names.RuntimeInvisibleAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { attachAnnotations(sym); } }, new AttributeReader(names.RuntimeInvisibleParameterAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { readParameterAnnotations(sym); } }, new AttributeReader(names.RuntimeVisibleAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { attachAnnotations(sym); } }, new AttributeReader(names.RuntimeVisibleParameterAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { readParameterAnnotations(sym); } }, // additional "legacy" v49 attributes, superceded by flags new AttributeReader(names.Annotation, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { sym.flags_field |= ANNOTATION; } }, new AttributeReader(names.Bridge, V49, MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { sym.flags_field |= BRIDGE; } }, new AttributeReader(names.Enum, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { sym.flags_field |= ENUM; } }, new AttributeReader(names.Varargs, V49, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { sym.flags_field |= VARARGS; } }, new AttributeReader(names.RuntimeVisibleTypeAnnotations, V52, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { attachTypeAnnotations(sym); } }, new AttributeReader(names.RuntimeInvisibleTypeAnnotations, V52, CLASS_OR_MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrLen) { attachTypeAnnotations(sym); } }, // The following attributes for a Code attribute are not currently handled // StackMapTable // SourceDebugExtension // LineNumberTable // LocalVariableTypeTable // standard v52 attributes new AttributeReader(names.MethodParameters, V52, MEMBER_ATTRIBUTE) { protected void read(Symbol sym, int attrlen) { int newbp = bp + attrlen; if (saveParameterNames) { sawMethodParameters = true; int numEntries = nextByte(); parameterNameIndices = new int[numEntries]; haveParameterNameIndices = true; int index = 0; for (int i = 0; i < numEntries; i++) { int nameIndex = nextChar(); int flags = nextChar(); if ((flags & (Flags.MANDATED | Flags.SYNTHETIC)) != 0) { continue; } parameterNameIndices[index++] = nameIndex; } } bp = newbp; } }, // standard v53 attributes new AttributeReader(names.Module, V53, CLASS_ATTRIBUTE) { @Override protected boolean accepts(AttributeKind kind) { return super.accepts(kind) && allowModules; } protected void read(Symbol sym, int attrLen) { if (sym.kind == TYP && sym.owner.kind == MDL) { ModuleSymbol msym = (ModuleSymbol) sym.owner; ListBuffer directives = new ListBuffer<>(); Name moduleName = readModuleName(nextChar()); if (currentModule.name != moduleName) { throw badClassFile("module.name.mismatch", moduleName, currentModule.name); } Set moduleFlags = readModuleFlags(nextChar()); msym.flags.addAll(moduleFlags); msym.version = readName(nextChar()); ListBuffer requires = new ListBuffer<>(); int nrequires = nextChar(); for (int i = 0; i < nrequires; i++) { ModuleSymbol rsym = syms.enterModule(readModuleName(nextChar())); Set flags = readRequiresFlags(nextChar()); if (rsym == syms.java_base && majorVersion >= V54.major) { if (flags.contains(RequiresFlag.TRANSITIVE)) { throw badClassFile("bad.requires.flag", RequiresFlag.TRANSITIVE); } if (flags.contains(RequiresFlag.STATIC_PHASE)) { throw badClassFile("bad.requires.flag", RequiresFlag.STATIC_PHASE); } } nextChar(); // skip compiled version requires.add(new RequiresDirective(rsym, flags)); } msym.requires = requires.toList(); directives.addAll(msym.requires); ListBuffer exports = new ListBuffer<>(); int nexports = nextChar(); for (int i = 0; i < nexports; i++) { Name n = readName(nextChar()); PackageSymbol p = syms.enterPackage(currentModule, names.fromUtf(internalize(n))); Set flags = readExportsFlags(nextChar()); int nto = nextChar(); List to; if (nto == 0) { to = null; } else { ListBuffer lb = new ListBuffer<>(); for (int t = 0; t < nto; t++) lb.append(syms.enterModule(readModuleName(nextChar()))); to = lb.toList(); } exports.add(new ExportsDirective(p, to, flags)); } msym.exports = exports.toList(); directives.addAll(msym.exports); ListBuffer opens = new ListBuffer<>(); int nopens = nextChar(); if (nopens != 0 && msym.flags.contains(ModuleFlags.OPEN)) { throw badClassFile("module.non.zero.opens", currentModule.name); } for (int i = 0; i < nopens; i++) { Name n = readName(nextChar()); PackageSymbol p = syms.enterPackage(currentModule, names.fromUtf(internalize(n))); Set flags = readOpensFlags(nextChar()); int nto = nextChar(); List to; if (nto == 0) { to = null; } else { ListBuffer lb = new ListBuffer<>(); for (int t = 0; t < nto; t++) lb.append(syms.enterModule(readModuleName(nextChar()))); to = lb.toList(); } opens.add(new OpensDirective(p, to, flags)); } msym.opens = opens.toList(); directives.addAll(msym.opens); msym.directives = directives.toList(); ListBuffer uses = new ListBuffer<>(); int nuses = nextChar(); for (int i = 0; i < nuses; i++) { Name srvc = readClassName(nextChar()); uses.add(new InterimUsesDirective(srvc)); } interimUses = uses.toList(); ListBuffer provides = new ListBuffer<>(); int nprovides = nextChar(); for (int p = 0; p < nprovides; p++) { Name srvc = readClassName(nextChar()); int nimpls = nextChar(); ListBuffer impls = new ListBuffer<>(); for (int i = 0; i < nimpls; i++) { impls.append(readClassName(nextChar())); provides.add(new InterimProvidesDirective(srvc, impls.toList())); } } interimProvides = provides.toList(); } } }, new AttributeReader(names.ModuleResolution, V53, CLASS_ATTRIBUTE) { @Override protected boolean accepts(AttributeKind kind) { return super.accepts(kind) && allowModules; } protected void read(Symbol sym, int attrLen) { if (sym.kind == TYP && sym.owner.kind == MDL) { ModuleSymbol msym = (ModuleSymbol) sym.owner; msym.resolutionFlags.addAll(readModuleResolutionFlags(nextChar())); } } }, }; for (AttributeReader r: readers) attributeReaders.put(r.name, r); } protected void readEnclosingMethodAttr(Symbol sym) { // sym is a nested class with an "Enclosing Method" attribute // remove sym from it's current owners scope and place it in // the scope specified by the attribute sym.owner.members().remove(sym); ClassSymbol self = (ClassSymbol)sym; ClassSymbol c = readClassSymbol(nextChar()); NameAndType nt = readNameAndType(nextChar()); if (c.members_field == null || c.kind != TYP) throw badClassFile("bad.enclosing.class", self, c); MethodSymbol m = findMethod(nt, c.members_field, self.flags()); if (nt != null && m == null) throw badEnclosingMethod(self); self.name = simpleBinaryName(self.flatname, c.flatname) ; self.owner = m != null ? m : c; if (self.name.isEmpty()) self.fullname = names.empty; else self.fullname = ClassSymbol.formFullName(self.name, self.owner); if (m != null) { ((ClassType)sym.type).setEnclosingType(m.type); } else if ((self.flags_field & STATIC) == 0) { ((ClassType)sym.type).setEnclosingType(c.type); } else { ((ClassType)sym.type).setEnclosingType(Type.noType); } enterTypevars(self, self.type); if (!missingTypeVariables.isEmpty()) { ListBuffer typeVars = new ListBuffer<>(); for (Type typevar : missingTypeVariables) { typeVars.append(findTypeVar(typevar.tsym.name)); } foundTypeVariables = typeVars.toList(); } else { foundTypeVariables = List.nil(); } } // See java.lang.Class private Name simpleBinaryName(Name self, Name enclosing) { String simpleBinaryName = self.toString().substring(enclosing.toString().length()); if (simpleBinaryName.length() < 1 || simpleBinaryName.charAt(0) != '$') throw badClassFile("bad.enclosing.method", self); int index = 1; while (index < simpleBinaryName.length() && isAsciiDigit(simpleBinaryName.charAt(index))) index++; return names.fromString(simpleBinaryName.substring(index)); } private MethodSymbol findMethod(NameAndType nt, Scope scope, long flags) { if (nt == null) return null; MethodType type = nt.uniqueType.type.asMethodType(); for (Symbol sym : scope.getSymbolsByName(nt.name)) { if (sym.kind == MTH && isSameBinaryType(sym.type.asMethodType(), type)) return (MethodSymbol)sym; } if (nt.name != names.init) // not a constructor return null; if ((flags & INTERFACE) != 0) // no enclosing instance return null; if (nt.uniqueType.type.getParameterTypes().isEmpty()) // no parameters return null; // A constructor of an inner class. // Remove the first argument (the enclosing instance) nt.setType(new MethodType(nt.uniqueType.type.getParameterTypes().tail, nt.uniqueType.type.getReturnType(), nt.uniqueType.type.getThrownTypes(), syms.methodClass)); // Try searching again return findMethod(nt, scope, flags); } /** Similar to Types.isSameType but avoids completion */ private boolean isSameBinaryType(MethodType mt1, MethodType mt2) { List types1 = types.erasure(mt1.getParameterTypes()) .prepend(types.erasure(mt1.getReturnType())); List types2 = mt2.getParameterTypes().prepend(mt2.getReturnType()); while (!types1.isEmpty() && !types2.isEmpty()) { if (types1.head.tsym != types2.head.tsym) return false; types1 = types1.tail; types2 = types2.tail; } return types1.isEmpty() && types2.isEmpty(); } /** * Character.isDigit answers true to some non-ascii * digits. This one does not. copied from java.lang.Class */ private static boolean isAsciiDigit(char c) { return '0' <= c && c <= '9'; } /** Read member attributes. */ void readMemberAttrs(Symbol sym) { readAttrs(sym, AttributeKind.MEMBER); } void readAttrs(Symbol sym, AttributeKind kind) { char ac = nextChar(); for (int i = 0; i < ac; i++) { Name attrName = readName(nextChar()); int attrLen = nextInt(); AttributeReader r = attributeReaders.get(attrName); if (r != null && r.accepts(kind)) r.read(sym, attrLen); else { bp = bp + attrLen; } } } private boolean readingClassAttr = false; private List missingTypeVariables = List.nil(); private List foundTypeVariables = List.nil(); /** Read class attributes. */ void readClassAttrs(ClassSymbol c) { readAttrs(c, AttributeKind.CLASS); } /** Read code block. */ Code readCode(Symbol owner) { nextChar(); // max_stack nextChar(); // max_locals final int code_length = nextInt(); bp += code_length; final char exception_table_length = nextChar(); bp += exception_table_length * 8; readMemberAttrs(owner); return null; } /************************************************************************ * Reading Java-language annotations ***********************************************************************/ /** * Save annotations. */ List readAnnotations() { int numAttributes = nextChar(); ListBuffer annotations = new ListBuffer<>(); for (int i = 0; i < numAttributes; i++) { annotations.append(readCompoundAnnotation()); } return annotations.toList(); } /** Attach annotations. */ void attachAnnotations(final Symbol sym) { attachAnnotations(sym, readAnnotations()); } /** * Attach annotations. */ void attachAnnotations(final Symbol sym, List annotations) { if (annotations.isEmpty()) { return; } ListBuffer proxies = new ListBuffer<>(); for (CompoundAnnotationProxy proxy : annotations) { if (proxy.type.tsym == syms.proprietaryType.tsym) sym.flags_field |= PROPRIETARY; else if (proxy.type.tsym == syms.profileType.tsym) { if (profile != Profile.DEFAULT) { for (Pair v : proxy.values) { if (v.fst == names.value && v.snd instanceof Attribute.Constant) { Attribute.Constant c = (Attribute.Constant)v.snd; if (c.type == syms.intType && ((Integer)c.value) > profile.value) { sym.flags_field |= NOT_IN_PROFILE; } } } } } else { if (proxy.type.tsym == syms.annotationTargetType.tsym) { target = proxy; } else if (proxy.type.tsym == syms.repeatableType.tsym) { repeatable = proxy; } else if (proxy.type.tsym == syms.deprecatedType.tsym) { sym.flags_field |= (DEPRECATED | DEPRECATED_ANNOTATION); for (Pair v : proxy.values) { if (v.fst == names.forRemoval && v.snd instanceof Attribute.Constant) { Attribute.Constant c = (Attribute.Constant)v.snd; if (c.type == syms.booleanType && ((Integer)c.value) != 0) { sym.flags_field |= DEPRECATED_REMOVAL; } } } } proxies.append(proxy); } } annotate.normal(new AnnotationCompleter(sym, proxies.toList())); } /** Read parameter annotations. */ void readParameterAnnotations(Symbol meth) { int numParameters = buf[bp++] & 0xFF; if (parameterAnnotations == null) { parameterAnnotations = new ParameterAnnotations[numParameters]; } else if (parameterAnnotations.length != numParameters) { throw badClassFile("bad.runtime.invisible.param.annotations", meth); } for (int pnum = 0; pnum < numParameters; pnum++) { if (parameterAnnotations[pnum] == null) { parameterAnnotations[pnum] = new ParameterAnnotations(); } parameterAnnotations[pnum].add(readAnnotations()); } } void attachTypeAnnotations(final Symbol sym) { int numAttributes = nextChar(); if (numAttributes != 0) { ListBuffer proxies = new ListBuffer<>(); for (int i = 0; i < numAttributes; i++) proxies.append(readTypeAnnotation()); annotate.normal(new TypeAnnotationCompleter(sym, proxies.toList())); } } /** Attach the default value for an annotation element. */ void attachAnnotationDefault(final Symbol sym) { final MethodSymbol meth = (MethodSymbol)sym; // only on methods final Attribute value = readAttributeValue(); // The default value is set later during annotation. It might // be the case that the Symbol sym is annotated _after_ the // repeating instances that depend on this default value, // because of this we set an interim value that tells us this // element (most likely) has a default. // // Set interim value for now, reset just before we do this // properly at annotate time. meth.defaultValue = value; annotate.normal(new AnnotationDefaultCompleter(meth, value)); } Type readTypeOrClassSymbol(int i) { // support preliminary jsr175-format class files if (buf[poolIdx[i]] == CONSTANT_Class) return readClassSymbol(i).type; return readTypeToProxy(i); } Type readEnumType(int i) { // support preliminary jsr175-format class files int index = poolIdx[i]; int length = getChar(index + 1); if (buf[index + length + 2] != ';') return enterClass(readName(i)).type; return readTypeToProxy(i); } Type readTypeToProxy(int i) { if (currentModule.module_info == currentOwner) { int index = poolIdx[i]; return new ProxyType(Arrays.copyOfRange(buf, index + 3, index + 3 + getChar(index + 1))); } else { return readType(i); } } CompoundAnnotationProxy readCompoundAnnotation() { Type t; if (currentModule.module_info == currentOwner) { int index = poolIdx[nextChar()]; t = new ProxyType(Arrays.copyOfRange(buf, index + 3, index + 3 + getChar(index + 1))); } else { t = readTypeOrClassSymbol(nextChar()); } int numFields = nextChar(); ListBuffer> pairs = new ListBuffer<>(); for (int i=0; i(name, value)); } return new CompoundAnnotationProxy(t, pairs.toList()); } TypeAnnotationProxy readTypeAnnotation() { TypeAnnotationPosition position = readPosition(); CompoundAnnotationProxy proxy = readCompoundAnnotation(); return new TypeAnnotationProxy(proxy, position); } TypeAnnotationPosition readPosition() { int tag = nextByte(); // TargetType tag is a byte if (!TargetType.isValidTargetTypeValue(tag)) throw badClassFile("bad.type.annotation.value", String.format("0x%02X", tag)); TargetType type = TargetType.fromTargetTypeValue(tag); switch (type) { // instanceof case INSTANCEOF: { final int offset = nextChar(); final TypeAnnotationPosition position = TypeAnnotationPosition.instanceOf(readTypePath()); position.offset = offset; return position; } // new expression case NEW: { final int offset = nextChar(); final TypeAnnotationPosition position = TypeAnnotationPosition.newObj(readTypePath()); position.offset = offset; return position; } // constructor/method reference receiver case CONSTRUCTOR_REFERENCE: { final int offset = nextChar(); final TypeAnnotationPosition position = TypeAnnotationPosition.constructorRef(readTypePath()); position.offset = offset; return position; } case METHOD_REFERENCE: { final int offset = nextChar(); final TypeAnnotationPosition position = TypeAnnotationPosition.methodRef(readTypePath()); position.offset = offset; return position; } // local variable case LOCAL_VARIABLE: { final int table_length = nextChar(); final int[] newLvarOffset = new int[table_length]; final int[] newLvarLength = new int[table_length]; final int[] newLvarIndex = new int[table_length]; for (int i = 0; i < table_length; ++i) { newLvarOffset[i] = nextChar(); newLvarLength[i] = nextChar(); newLvarIndex[i] = nextChar(); } final TypeAnnotationPosition position = TypeAnnotationPosition.localVariable(readTypePath()); position.lvarOffset = newLvarOffset; position.lvarLength = newLvarLength; position.lvarIndex = newLvarIndex; return position; } // resource variable case RESOURCE_VARIABLE: { final int table_length = nextChar(); final int[] newLvarOffset = new int[table_length]; final int[] newLvarLength = new int[table_length]; final int[] newLvarIndex = new int[table_length]; for (int i = 0; i < table_length; ++i) { newLvarOffset[i] = nextChar(); newLvarLength[i] = nextChar(); newLvarIndex[i] = nextChar(); } final TypeAnnotationPosition position = TypeAnnotationPosition.resourceVariable(readTypePath()); position.lvarOffset = newLvarOffset; position.lvarLength = newLvarLength; position.lvarIndex = newLvarIndex; return position; } // exception parameter case EXCEPTION_PARAMETER: { final int exception_index = nextChar(); final TypeAnnotationPosition position = TypeAnnotationPosition.exceptionParameter(readTypePath()); position.setExceptionIndex(exception_index); return position; } // method receiver case METHOD_RECEIVER: return TypeAnnotationPosition.methodReceiver(readTypePath()); // type parameter case CLASS_TYPE_PARAMETER: { final int parameter_index = nextByte(); return TypeAnnotationPosition .typeParameter(readTypePath(), parameter_index); } case METHOD_TYPE_PARAMETER: { final int parameter_index = nextByte(); return TypeAnnotationPosition .methodTypeParameter(readTypePath(), parameter_index); } // type parameter bound case CLASS_TYPE_PARAMETER_BOUND: { final int parameter_index = nextByte(); final int bound_index = nextByte(); return TypeAnnotationPosition .typeParameterBound(readTypePath(), parameter_index, bound_index); } case METHOD_TYPE_PARAMETER_BOUND: { final int parameter_index = nextByte(); final int bound_index = nextByte(); return TypeAnnotationPosition .methodTypeParameterBound(readTypePath(), parameter_index, bound_index); } // class extends or implements clause case CLASS_EXTENDS: { final int type_index = nextChar(); return TypeAnnotationPosition.classExtends(readTypePath(), type_index); } // throws case THROWS: { final int type_index = nextChar(); return TypeAnnotationPosition.methodThrows(readTypePath(), type_index); } // method parameter case METHOD_FORMAL_PARAMETER: { final int parameter_index = nextByte(); return TypeAnnotationPosition.methodParameter(readTypePath(), parameter_index); } // type cast case CAST: { final int offset = nextChar(); final int type_index = nextByte(); final TypeAnnotationPosition position = TypeAnnotationPosition.typeCast(readTypePath(), type_index); position.offset = offset; return position; } // method/constructor/reference type argument case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: { final int offset = nextChar(); final int type_index = nextByte(); final TypeAnnotationPosition position = TypeAnnotationPosition .constructorInvocationTypeArg(readTypePath(), type_index); position.offset = offset; return position; } case METHOD_INVOCATION_TYPE_ARGUMENT: { final int offset = nextChar(); final int type_index = nextByte(); final TypeAnnotationPosition position = TypeAnnotationPosition .methodInvocationTypeArg(readTypePath(), type_index); position.offset = offset; return position; } case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: { final int offset = nextChar(); final int type_index = nextByte(); final TypeAnnotationPosition position = TypeAnnotationPosition .constructorRefTypeArg(readTypePath(), type_index); position.offset = offset; return position; } case METHOD_REFERENCE_TYPE_ARGUMENT: { final int offset = nextChar(); final int type_index = nextByte(); final TypeAnnotationPosition position = TypeAnnotationPosition .methodRefTypeArg(readTypePath(), type_index); position.offset = offset; return position; } // We don't need to worry about these case METHOD_RETURN: return TypeAnnotationPosition.methodReturn(readTypePath()); case FIELD: return TypeAnnotationPosition.field(readTypePath()); case UNKNOWN: throw new AssertionError("jvm.ClassReader: UNKNOWN target type should never occur!"); default: throw new AssertionError("jvm.ClassReader: Unknown target type for position: " + type); } } List readTypePath() { int len = nextByte(); ListBuffer loc = new ListBuffer<>(); for (int i = 0; i < len * TypeAnnotationPosition.TypePathEntry.bytesPerEntry; ++i) loc = loc.append(nextByte()); return TypeAnnotationPosition.getTypePathFromBinary(loc.toList()); } Attribute readAttributeValue() { char c = (char) buf[bp++]; switch (c) { case 'B': return new Attribute.Constant(syms.byteType, readPool(nextChar())); case 'C': return new Attribute.Constant(syms.charType, readPool(nextChar())); case 'D': return new Attribute.Constant(syms.doubleType, readPool(nextChar())); case 'F': return new Attribute.Constant(syms.floatType, readPool(nextChar())); case 'I': return new Attribute.Constant(syms.intType, readPool(nextChar())); case 'J': return new Attribute.Constant(syms.longType, readPool(nextChar())); case 'S': return new Attribute.Constant(syms.shortType, readPool(nextChar())); case 'Z': return new Attribute.Constant(syms.booleanType, readPool(nextChar())); case 's': return new Attribute.Constant(syms.stringType, readPool(nextChar()).toString()); case 'e': return new EnumAttributeProxy(readEnumType(nextChar()), readName(nextChar())); case 'c': return new ClassAttributeProxy(readTypeOrClassSymbol(nextChar())); case '[': { int n = nextChar(); ListBuffer l = new ListBuffer<>(); for (int i=0; i values; ArrayAttributeProxy(List values) { super(null); this.values = values; } public void accept(Visitor v) { ((ProxyVisitor)v).visitArrayAttributeProxy(this); } @Override @DefinedBy(Api.LANGUAGE_MODEL) public String toString() { return "{" + values + "}"; } } /** A temporary proxy representing a compound attribute. */ static class CompoundAnnotationProxy extends Attribute { final List> values; public CompoundAnnotationProxy(Type type, List> values) { super(type); this.values = values; } public void accept(Visitor v) { ((ProxyVisitor)v).visitCompoundAnnotationProxy(this); } @Override @DefinedBy(Api.LANGUAGE_MODEL) public String toString() { StringBuilder buf = new StringBuilder(); buf.append("@"); buf.append(type.tsym.getQualifiedName()); buf.append("/*proxy*/{"); boolean first = true; for (List> v = values; v.nonEmpty(); v = v.tail) { Pair value = v.head; if (!first) buf.append(","); first = false; buf.append(value.fst); buf.append("="); buf.append(value.snd); } buf.append("}"); return buf.toString(); } } /** A temporary proxy representing a type annotation. */ static class TypeAnnotationProxy { final CompoundAnnotationProxy compound; final TypeAnnotationPosition position; public TypeAnnotationProxy(CompoundAnnotationProxy compound, TypeAnnotationPosition position) { this.compound = compound; this.position = position; } } class AnnotationDeproxy implements ProxyVisitor { private ClassSymbol requestingOwner; AnnotationDeproxy(ClassSymbol owner) { this.requestingOwner = owner; } List deproxyCompoundList(List pl) { // also must fill in types!!!! ListBuffer buf = new ListBuffer<>(); for (List l = pl; l.nonEmpty(); l=l.tail) { buf.append(deproxyCompound(l.head)); } return buf.toList(); } Attribute.Compound deproxyCompound(CompoundAnnotationProxy a) { Type annotationType = resolvePossibleProxyType(a.type); ListBuffer> buf = new ListBuffer<>(); for (List> l = a.values; l.nonEmpty(); l = l.tail) { MethodSymbol meth = findAccessMethod(annotationType, l.head.fst); buf.append(new Pair<>(meth, deproxy(meth.type.getReturnType(), l.head.snd))); } return new Attribute.Compound(annotationType, buf.toList()); } MethodSymbol findAccessMethod(Type container, Name name) { CompletionFailure failure = null; try { for (Symbol sym : container.tsym.members().getSymbolsByName(name)) { if (sym.kind == MTH && sym.type.getParameterTypes().length() == 0) return (MethodSymbol) sym; } } catch (CompletionFailure ex) { failure = ex; } // The method wasn't found: emit a warning and recover JavaFileObject prevSource = log.useSource(requestingOwner.classfile); try { if (lintClassfile) { if (failure == null) { log.warning(Warnings.AnnotationMethodNotFound(container, name)); } else { log.warning(Warnings.AnnotationMethodNotFoundReason(container, name, failure.getDetailValue()));//diagnostic, if present } } } finally { log.useSource(prevSource); } // Construct a new method type and symbol. Use bottom // type (typeof null) as return type because this type is // a subtype of all reference types and can be converted // to primitive types by unboxing. MethodType mt = new MethodType(List.nil(), syms.botType, List.nil(), syms.methodClass); return new MethodSymbol(PUBLIC | ABSTRACT, name, mt, container.tsym); } Attribute result; Type type; Attribute deproxy(Type t, Attribute a) { Type oldType = type; try { type = t; a.accept(this); return result; } finally { type = oldType; } } // implement Attribute.Visitor below public void visitConstant(Attribute.Constant value) { // assert value.type == type; result = value; } public void visitClass(Attribute.Class clazz) { result = clazz; } public void visitEnum(Attribute.Enum e) { throw new AssertionError(); // shouldn't happen } public void visitCompound(Attribute.Compound compound) { throw new AssertionError(); // shouldn't happen } public void visitArray(Attribute.Array array) { throw new AssertionError(); // shouldn't happen } public void visitError(Attribute.Error e) { throw new AssertionError(); // shouldn't happen } public void visitEnumAttributeProxy(EnumAttributeProxy proxy) { // type.tsym.flatName() should == proxy.enumFlatName Type enumType = resolvePossibleProxyType(proxy.enumType); TypeSymbol enumTypeSym = enumType.tsym; VarSymbol enumerator = null; CompletionFailure failure = null; try { for (Symbol sym : enumTypeSym.members().getSymbolsByName(proxy.enumerator)) { if (sym.kind == VAR) { enumerator = (VarSymbol)sym; break; } } } catch (CompletionFailure ex) { failure = ex; } if (enumerator == null) { if (failure != null) { log.warning(Warnings.UnknownEnumConstantReason(currentClassFile, enumTypeSym, proxy.enumerator, failure.getDiagnostic())); } else { log.warning(Warnings.UnknownEnumConstant(currentClassFile, enumTypeSym, proxy.enumerator)); } result = new Attribute.Enum(enumTypeSym.type, new VarSymbol(0, proxy.enumerator, syms.botType, enumTypeSym)); } else { result = new Attribute.Enum(enumTypeSym.type, enumerator); } } @Override public void visitClassAttributeProxy(ClassAttributeProxy proxy) { Type classType = resolvePossibleProxyType(proxy.classType); result = new Attribute.Class(types, classType); } public void visitArrayAttributeProxy(ArrayAttributeProxy proxy) { int length = proxy.values.length(); Attribute[] ats = new Attribute[length]; Type elemtype = types.elemtype(type); int i = 0; for (List p = proxy.values; p.nonEmpty(); p = p.tail) { ats[i++] = deproxy(elemtype, p.head); } result = new Attribute.Array(type, ats); } public void visitCompoundAnnotationProxy(CompoundAnnotationProxy proxy) { result = deproxyCompound(proxy); } Type resolvePossibleProxyType(Type t) { if (t instanceof ProxyType) { Assert.check(requestingOwner.owner.kind == MDL); ModuleSymbol prevCurrentModule = currentModule; currentModule = (ModuleSymbol) requestingOwner.owner; try { return ((ProxyType) t).resolve(); } finally { currentModule = prevCurrentModule; } } else { return t; } } } class AnnotationDefaultCompleter extends AnnotationDeproxy implements Runnable { final MethodSymbol sym; final Attribute value; final JavaFileObject classFile = currentClassFile; AnnotationDefaultCompleter(MethodSymbol sym, Attribute value) { super(currentOwner.kind == MTH ? currentOwner.enclClass() : (ClassSymbol)currentOwner); this.sym = sym; this.value = value; } @Override public void run() { JavaFileObject previousClassFile = currentClassFile; try { // Reset the interim value set earlier in // attachAnnotationDefault(). sym.defaultValue = null; currentClassFile = classFile; sym.defaultValue = deproxy(sym.type.getReturnType(), value); } finally { currentClassFile = previousClassFile; } } @Override public String toString() { return " ClassReader store default for " + sym.owner + "." + sym + " is " + value; } } class AnnotationCompleter extends AnnotationDeproxy implements Runnable { final Symbol sym; final List l; final JavaFileObject classFile; AnnotationCompleter(Symbol sym, List l) { super(currentOwner.kind == MTH ? currentOwner.enclClass() : (ClassSymbol)currentOwner); if (sym.kind == TYP && sym.owner.kind == MDL) { this.sym = sym.owner; } else { this.sym = sym; } this.l = l; this.classFile = currentClassFile; } @Override public void run() { JavaFileObject previousClassFile = currentClassFile; try { currentClassFile = classFile; List newList = deproxyCompoundList(l); for (Attribute.Compound attr : newList) { if (attr.type.tsym == syms.deprecatedType.tsym) { sym.flags_field |= (DEPRECATED | DEPRECATED_ANNOTATION); Attribute forRemoval = attr.member(names.forRemoval); if (forRemoval instanceof Attribute.Constant) { Attribute.Constant c = (Attribute.Constant) forRemoval; if (c.type == syms.booleanType && ((Integer) c.value) != 0) { sym.flags_field |= DEPRECATED_REMOVAL; } } } } if (sym.annotationsPendingCompletion()) { sym.setDeclarationAttributes(newList); } else { sym.appendAttributes(newList); } } finally { currentClassFile = previousClassFile; } } @Override public String toString() { return " ClassReader annotate " + sym.owner + "." + sym + " with " + l; } } class TypeAnnotationCompleter extends AnnotationCompleter { List proxies; TypeAnnotationCompleter(Symbol sym, List proxies) { super(sym, List.nil()); this.proxies = proxies; } List deproxyTypeCompoundList(List proxies) { ListBuffer buf = new ListBuffer<>(); for (TypeAnnotationProxy proxy: proxies) { Attribute.Compound compound = deproxyCompound(proxy.compound); Attribute.TypeCompound typeCompound = new Attribute.TypeCompound(compound, proxy.position); buf.add(typeCompound); } return buf.toList(); } @Override public void run() { JavaFileObject previousClassFile = currentClassFile; try { currentClassFile = classFile; List newList = deproxyTypeCompoundList(proxies); sym.setTypeAttributes(newList.prependList(sym.getRawTypeAttributes())); } finally { currentClassFile = previousClassFile; } } } /************************************************************************ * Reading Symbols ***********************************************************************/ /** Read a field. */ VarSymbol readField() { long flags = adjustFieldFlags(nextChar()); Name name = readName(nextChar()); Type type = readType(nextChar()); VarSymbol v = new VarSymbol(flags, name, type, currentOwner); readMemberAttrs(v); return v; } /** Read a method. */ MethodSymbol readMethod() { long flags = adjustMethodFlags(nextChar()); Name name = readName(nextChar()); Type type = readType(nextChar()); if (currentOwner.isInterface() && (flags & ABSTRACT) == 0 && !name.equals(names.clinit)) { if (majorVersion > Version.V52.major || (majorVersion == Version.V52.major && minorVersion >= Version.V52.minor)) { if ((flags & (STATIC | PRIVATE)) == 0) { currentOwner.flags_field |= DEFAULT; flags |= DEFAULT | ABSTRACT; } } else { //protect against ill-formed classfiles throw badClassFile((flags & STATIC) == 0 ? "invalid.default.interface" : "invalid.static.interface", Integer.toString(majorVersion), Integer.toString(minorVersion)); } } if (name == names.init && currentOwner.hasOuterInstance()) { // Sometimes anonymous classes don't have an outer // instance, however, there is no reliable way to tell so // we never strip this$n // ditto for local classes. Local classes that have an enclosing method set // won't pass the "hasOuterInstance" check above, but those that don't have an // enclosing method (i.e. from initializers) will pass that check. boolean local = !currentOwner.owner.members().includes(currentOwner, LookupKind.NON_RECURSIVE); if (!currentOwner.name.isEmpty() && !local) type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()), type.getReturnType(), type.getThrownTypes(), syms.methodClass); } MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner); if (types.isSignaturePolymorphic(m)) { m.flags_field |= SIGNATURE_POLYMORPHIC; } if (saveParameterNames) initParameterNames(m); Symbol prevOwner = currentOwner; currentOwner = m; try { readMemberAttrs(m); } finally { currentOwner = prevOwner; } setParameters(m, type); if ((flags & VARARGS) != 0) { final Type last = type.getParameterTypes().last(); if (last == null || !last.hasTag(ARRAY)) { m.flags_field &= ~VARARGS; throw badClassFile("malformed.vararg.method", m); } } return m; } private List adjustMethodParams(long flags, List args) { boolean isVarargs = (flags & VARARGS) != 0; if (isVarargs) { Type varargsElem = args.last(); ListBuffer adjustedArgs = new ListBuffer<>(); for (Type t : args) { adjustedArgs.append(t != varargsElem ? t : ((ArrayType)t).makeVarargs()); } args = adjustedArgs.toList(); } return args.tail; } /** * Init the parameter names array. * Parameter names are currently inferred from the names in the * LocalVariableTable attributes of a Code attribute. * (Note: this means parameter names are currently not available for * methods without a Code attribute.) * This method initializes an array in which to store the name indexes * of parameter names found in LocalVariableTable attributes. It is * slightly supersized to allow for additional slots with a start_pc of 0. */ void initParameterNames(MethodSymbol sym) { // make allowance for synthetic parameters. final int excessSlots = 4; int expectedParameterSlots = Code.width(sym.type.getParameterTypes()) + excessSlots; if (parameterNameIndices == null || parameterNameIndices.length < expectedParameterSlots) { parameterNameIndices = new int[expectedParameterSlots]; } else Arrays.fill(parameterNameIndices, 0); haveParameterNameIndices = false; sawMethodParameters = false; } /** * Set the parameters for a method symbol, including any names and * annotations that were read. * *

The type of the symbol may have changed while reading the * method attributes (see the Signature attribute). This may be * because of generic information or because anonymous synthetic * parameters were added. The original type (as read from the * method descriptor) is used to help guess the existence of * anonymous synthetic parameters. */ void setParameters(MethodSymbol sym, Type jvmType) { // If we get parameter names from MethodParameters, then we // don't need to skip. int firstParam = 0; if (!sawMethodParameters) { firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; // the code in readMethod may have skipped the first // parameter when setting up the MethodType. If so, we // make a corresponding allowance here for the position of // the first parameter. Note that this assumes the // skipped parameter has a width of 1 -- i.e. it is not // a double width type (long or double.) if (sym.name == names.init && currentOwner.hasOuterInstance()) { // Sometimes anonymous classes don't have an outer // instance, however, there is no reliable way to tell so // we never strip this$n if (!currentOwner.name.isEmpty()) firstParam += 1; } if (sym.type != jvmType) { // reading the method attributes has caused the // symbol's type to be changed. (i.e. the Signature // attribute.) This may happen if there are hidden // (synthetic) parameters in the descriptor, but not // in the Signature. The position of these hidden // parameters is unspecified; for now, assume they are // at the beginning, and so skip over them. The // primary case for this is two hidden parameters // passed into Enum constructors. int skip = Code.width(jvmType.getParameterTypes()) - Code.width(sym.type.getParameterTypes()); firstParam += skip; } } List paramNames = List.nil(); ListBuffer params = new ListBuffer<>(); int nameIndex = firstParam; int annotationIndex = 0; for (Type t: sym.type.getParameterTypes()) { Name name = parameterName(nameIndex, paramNames); paramNames = paramNames.prepend(name); VarSymbol param = new VarSymbol(PARAMETER, name, t, sym); params.append(param); if (parameterAnnotations != null) { ParameterAnnotations annotations = parameterAnnotations[annotationIndex]; if (annotations != null && annotations.proxies != null && !annotations.proxies.isEmpty()) { annotate.normal(new AnnotationCompleter(param, annotations.proxies)); } } nameIndex += sawMethodParameters ? 1 : Code.width(t); annotationIndex++; } if (parameterAnnotations != null && parameterAnnotations.length != annotationIndex) { throw badClassFile("bad.runtime.invisible.param.annotations", sym); } Assert.checkNull(sym.params); sym.params = params.toList(); parameterAnnotations = null; parameterNameIndices = null; } // Returns the name for the parameter at position 'index', either using // names read from the MethodParameters, or by synthesizing a name that // is not on the 'exclude' list. private Name parameterName(int index, List exclude) { if (parameterNameIndices != null && index < parameterNameIndices.length && parameterNameIndices[index] != 0) { return readName(parameterNameIndices[index]); } String prefix = "arg"; while (true) { Name argName = names.fromString(prefix + exclude.size()); if (!exclude.contains(argName)) return argName; prefix += "$"; } } /** * skip n bytes */ void skipBytes(int n) { bp = bp + n; } /** Skip a field or method */ void skipMember() { bp = bp + 6; char ac = nextChar(); for (int i = 0; i < ac; i++) { bp = bp + 2; int attrLen = nextInt(); bp = bp + attrLen; } } void skipInnerClasses() { int n = nextChar(); for (int i = 0; i < n; i++) { nextChar(); nextChar(); nextChar(); nextChar(); } } /** Enter type variables of this classtype and all enclosing ones in * `typevars'. */ protected void enterTypevars(Symbol sym, Type t) { if (t.getEnclosingType() != null) { if (!t.getEnclosingType().hasTag(TypeTag.NONE)) { enterTypevars(sym.owner, t.getEnclosingType()); } } else if (sym.kind == MTH && !sym.isStatic()) { enterTypevars(sym.owner, sym.owner.type); } for (List xs = t.getTypeArguments(); xs.nonEmpty(); xs = xs.tail) { typevars.enter(xs.head.tsym); } } protected ClassSymbol enterClass(Name name) { if (syms.proprietaryType.tsym.flatName() == name) return (ClassSymbol) syms.proprietaryType.tsym; return syms.enterClass(currentModule, name); } protected ClassSymbol enterClass(Name name, TypeSymbol owner) { return syms.enterClass(currentModule, name, owner); } /** Read contents of a given class symbol `c'. Both external and internal * versions of an inner class are read. */ void readClass(ClassSymbol c) { ClassType ct = (ClassType)c.type; // allocate scope for members c.members_field = WriteableScope.create(c); // prepare type variable table typevars = typevars.dup(currentOwner); if (ct.getEnclosingType().hasTag(CLASS)) enterTypevars(c.owner, ct.getEnclosingType()); // read flags, or skip if this is an inner class long f = nextChar(); long flags = adjustClassFlags(f); if ((flags & MODULE) == 0) { if (c.owner.kind == PCK || c.owner.kind == ERR) c.flags_field = flags; // read own class name and check that it matches currentModule = c.packge().modle; ClassSymbol self = readClassSymbol(nextChar()); if (c != self) { throw badClassFile("class.file.wrong.class", self.flatname); } } else { if (majorVersion < Version.V53.major) { throw badClassFile("anachronistic.module.info", Integer.toString(majorVersion), Integer.toString(minorVersion)); } c.flags_field = flags; currentModule = (ModuleSymbol) c.owner; int this_class = nextChar(); // temp, no check on this_class } // class attributes must be read before class // skip ahead to read class attributes int startbp = bp; nextChar(); char interfaceCount = nextChar(); bp += interfaceCount * 2; char fieldCount = nextChar(); for (int i = 0; i < fieldCount; i++) skipMember(); char methodCount = nextChar(); for (int i = 0; i < methodCount; i++) skipMember(); readClassAttrs(c); if (readAllOfClassFile) { for (int i = 1; i < poolObj.length; i++) readPool(i); c.pool = new Pool(poolObj.length, poolObj, types); } // reset and read rest of classinfo bp = startbp; int n = nextChar(); if ((flags & MODULE) != 0 && n > 0) { throw badClassFile("module.info.invalid.super.class"); } if (ct.supertype_field == null) ct.supertype_field = (n == 0) ? Type.noType : readClassSymbol(n).erasure(types); n = nextChar(); List is = List.nil(); for (int i = 0; i < n; i++) { Type _inter = readClassSymbol(nextChar()).erasure(types); is = is.prepend(_inter); } if (ct.interfaces_field == null) ct.interfaces_field = is.reverse(); Assert.check(fieldCount == nextChar()); for (int i = 0; i < fieldCount; i++) enterMember(c, readField()); Assert.check(methodCount == nextChar()); for (int i = 0; i < methodCount; i++) enterMember(c, readMethod()); typevars = typevars.leave(); } /** Read inner class info. For each inner/outer pair allocate a * member class. */ void readInnerClasses(ClassSymbol c) { int n = nextChar(); for (int i = 0; i < n; i++) { nextChar(); // skip inner class symbol ClassSymbol outer = readClassSymbol(nextChar()); Name name = readName(nextChar()); if (name == null) name = names.empty; long flags = adjustClassFlags(nextChar()); if (outer != null) { // we have a member class if (name == names.empty) name = names.one; ClassSymbol member = enterClass(name, outer); if ((flags & STATIC) == 0) { ((ClassType)member.type).setEnclosingType(outer.type); if (member.erasure_field != null) ((ClassType)member.erasure_field).setEnclosingType(types.erasure(outer.type)); } if (c == outer) { member.flags_field = flags; enterMember(c, member); } } } } /** Read a class definition from the bytes in buf. */ private void readClassBuffer(ClassSymbol c) throws IOException { int magic = nextInt(); if (magic != JAVA_MAGIC) throw badClassFile("illegal.start.of.class.file"); minorVersion = nextChar(); majorVersion = nextChar(); int maxMajor = Version.MAX().major; int maxMinor = Version.MAX().minor; if (majorVersion > maxMajor || majorVersion * 1000 + minorVersion < Version.MIN().major * 1000 + Version.MIN().minor) { if (majorVersion == (maxMajor + 1)) log.warning(Warnings.BigMajorVersion(currentClassFile, majorVersion, maxMajor)); else throw badClassFile("wrong.version", Integer.toString(majorVersion), Integer.toString(minorVersion), Integer.toString(maxMajor), Integer.toString(maxMinor)); } indexPool(); if (signatureBuffer.length < bp) { int ns = Integer.highestOneBit(bp) << 1; signatureBuffer = new byte[ns]; } readClass(c); } public void readClassFile(ClassSymbol c) { currentOwner = c; currentClassFile = c.classfile; warnedAttrs.clear(); filling = true; target = null; repeatable = null; try { bp = 0; buf = readInputStream(buf, c.classfile.openInputStream()); readClassBuffer(c); if (!missingTypeVariables.isEmpty() && !foundTypeVariables.isEmpty()) { List missing = missingTypeVariables; List found = foundTypeVariables; missingTypeVariables = List.nil(); foundTypeVariables = List.nil(); interimUses = List.nil(); interimProvides = List.nil(); filling = false; ClassType ct = (ClassType)currentOwner.type; ct.supertype_field = types.subst(ct.supertype_field, missing, found); ct.interfaces_field = types.subst(ct.interfaces_field, missing, found); ct.typarams_field = types.substBounds(ct.typarams_field, missing, found); for (List types = ct.typarams_field; types.nonEmpty(); types = types.tail) { types.head.tsym.type = types.head; } } else if (missingTypeVariables.isEmpty() != foundTypeVariables.isEmpty()) { Name name = missingTypeVariables.head.tsym.name; throw badClassFile("undecl.type.var", name); } if ((c.flags_field & Flags.ANNOTATION) != 0) { c.setAnnotationTypeMetadata(new AnnotationTypeMetadata(c, new CompleterDeproxy(c, target, repeatable))); } else { c.setAnnotationTypeMetadata(AnnotationTypeMetadata.notAnAnnotationType()); } if (c == currentModule.module_info) { if (interimUses.nonEmpty() || interimProvides.nonEmpty()) { Assert.check(currentModule.isCompleted()); currentModule.usesProvidesCompleter = new UsesProvidesCompleter(currentModule, interimUses, interimProvides); } else { currentModule.uses = List.nil(); currentModule.provides = List.nil(); } } } catch (IOException | ClosedFileSystemException ex) { throw badClassFile("unable.to.access.file", ex.toString()); } catch (ArrayIndexOutOfBoundsException ex) { throw badClassFile("bad.class.file", c.flatname); } finally { interimUses = List.nil(); interimProvides = List.nil(); missingTypeVariables = List.nil(); foundTypeVariables = List.nil(); filling = false; } } // where private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException { try { buf = ensureCapacity(buf, s.available()); int r = s.read(buf); int bp = 0; while (r != -1) { bp += r; buf = ensureCapacity(buf, bp); r = s.read(buf, bp, buf.length - bp); } return buf; } finally { try { s.close(); } catch (IOException e) { /* Ignore any errors, as this stream may have already * thrown a related exception which is the one that * should be reported. */ } } } /* * ensureCapacity will increase the buffer as needed, taking note that * the new buffer will always be greater than the needed and never * exactly equal to the needed size or bp. If equal then the read (above) * will infinitely loop as buf.length - bp == 0. */ private static byte[] ensureCapacity(byte[] buf, int needed) { if (buf.length <= needed) { byte[] old = buf; buf = new byte[Integer.highestOneBit(needed) << 1]; System.arraycopy(old, 0, buf, 0, old.length); } return buf; } /** We can only read a single class file at a time; this * flag keeps track of when we are currently reading a class * file. */ public boolean filling = false; /************************************************************************ * Adjusting flags ***********************************************************************/ long adjustFieldFlags(long flags) { return flags; } long adjustMethodFlags(long flags) { if ((flags & ACC_BRIDGE) != 0) { flags &= ~ACC_BRIDGE; flags |= BRIDGE; } if ((flags & ACC_VARARGS) != 0) { flags &= ~ACC_VARARGS; flags |= VARARGS; } return flags; } long adjustClassFlags(long flags) { if ((flags & ACC_MODULE) != 0) { flags &= ~ACC_MODULE; flags |= MODULE; } return flags & ~ACC_SUPER; // SUPER and SYNCHRONIZED bits overloaded } /** * A subclass of JavaFileObject for the sourcefile attribute found in a classfile. * The attribute is only the last component of the original filename, so is unlikely * to be valid as is, so operations other than those to access the name throw * UnsupportedOperationException */ private static class SourceFileObject implements JavaFileObject { /** The file's name. */ private final Name name; private final Name flatname; public SourceFileObject(Name name, Name flatname) { this.name = name; this.flatname = flatname; } @Override @DefinedBy(Api.COMPILER) public URI toUri() { try { return new URI(null, name.toString(), null); } catch (URISyntaxException e) { throw new PathFileObject.CannotCreateUriError(name.toString(), e); } } @Override @DefinedBy(Api.COMPILER) public String getName() { return name.toString(); } @Override @DefinedBy(Api.COMPILER) public JavaFileObject.Kind getKind() { return BaseFileManager.getKind(getName()); } @Override @DefinedBy(Api.COMPILER) public InputStream openInputStream() { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public OutputStream openOutputStream() { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public CharBuffer getCharContent(boolean ignoreEncodingErrors) { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public Reader openReader(boolean ignoreEncodingErrors) { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public Writer openWriter() { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public long getLastModified() { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public boolean delete() { throw new UnsupportedOperationException(); } @Override @DefinedBy(Api.COMPILER) public boolean isNameCompatible(String simpleName, JavaFileObject.Kind kind) { return true; // fail-safe mode } @Override @DefinedBy(Api.COMPILER) public NestingKind getNestingKind() { return null; } @Override @DefinedBy(Api.COMPILER) public Modifier getAccessLevel() { return null; } /** * Check if two file objects are equal. * SourceFileObjects are just placeholder objects for the value of a * SourceFile attribute, and do not directly represent specific files. * Two SourceFileObjects are equal if their names are equal. */ @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof SourceFileObject)) return false; SourceFileObject o = (SourceFileObject) other; return name.equals(o.name); } @Override public int hashCode() { return name.hashCode(); } } private class CompleterDeproxy implements AnnotationTypeCompleter { ClassSymbol proxyOn; CompoundAnnotationProxy target; CompoundAnnotationProxy repeatable; public CompleterDeproxy(ClassSymbol c, CompoundAnnotationProxy target, CompoundAnnotationProxy repeatable) { this.proxyOn = c; this.target = target; this.repeatable = repeatable; } @Override public void complete(ClassSymbol sym) { Assert.check(proxyOn == sym); Attribute.Compound theTarget = null, theRepeatable = null; AnnotationDeproxy deproxy; try { if (target != null) { deproxy = new AnnotationDeproxy(proxyOn); theTarget = deproxy.deproxyCompound(target); } if (repeatable != null) { deproxy = new AnnotationDeproxy(proxyOn); theRepeatable = deproxy.deproxyCompound(repeatable); } } catch (Exception e) { throw new CompletionFailure(sym, ClassReader.this.diagFactory.fragment(Fragments.ExceptionMessage(e.getMessage())), dcfh); } sym.getAnnotationTypeMetadata().setTarget(theTarget); sym.getAnnotationTypeMetadata().setRepeatable(theRepeatable); } } private class ProxyType extends Type { private final byte[] content; public ProxyType(byte[] content) { super(syms.noSymbol, TypeMetadata.EMPTY); this.content = content; } @Override public TypeTag getTag() { return TypeTag.NONE; } @Override public Type cloneWithMetadata(TypeMetadata metadata) { throw new UnsupportedOperationException(); } public Type resolve() { return sigToType(content, 0, content.length); } @Override @DefinedBy(Api.LANGUAGE_MODEL) public String toString() { return ""; } } private static final class InterimUsesDirective { public final Name service; public InterimUsesDirective(Name service) { this.service = service; } } private static final class InterimProvidesDirective { public final Name service; public final List impls; public InterimProvidesDirective(Name service, List impls) { this.service = service; this.impls = impls; } } private final class UsesProvidesCompleter implements Completer { private final ModuleSymbol currentModule; private final List interimUsesCopy; private final List interimProvidesCopy; public UsesProvidesCompleter(ModuleSymbol currentModule, List interimUsesCopy, List interimProvidesCopy) { this.currentModule = currentModule; this.interimUsesCopy = interimUsesCopy; this.interimProvidesCopy = interimProvidesCopy; } @Override public void complete(Symbol sym) throws CompletionFailure { ListBuffer directives = new ListBuffer<>(); directives.addAll(currentModule.directives); ListBuffer uses = new ListBuffer<>(); for (InterimUsesDirective interim : interimUsesCopy) { UsesDirective d = new UsesDirective(syms.enterClass(currentModule, interim.service)); uses.add(d); directives.add(d); } currentModule.uses = uses.toList(); ListBuffer provides = new ListBuffer<>(); for (InterimProvidesDirective interim : interimProvidesCopy) { ListBuffer impls = new ListBuffer<>(); for (Name impl : interim.impls) { impls.append(syms.enterClass(currentModule, impl)); } ProvidesDirective d = new ProvidesDirective(syms.enterClass(currentModule, interim.service), impls.toList()); provides.add(d); directives.add(d); } currentModule.provides = provides.toList(); currentModule.directives = directives.toList(); } } }