void uncaughtType(Type t, Position pos) throws SemanticException { SemanticException e = new SemanticException( codeType + " cannot throw a \"" + t + "\"; the exception must either be caught or declared to be thrown.", pos); Map<String, Object> map = CollectionFactory.newHashMap(); map.put(CodedErrorInfo.ERROR_CODE_KEY, CodedErrorInfo.ERROR_CODE_SURROUND_THROW); map.put("TYPE", t.toString()); e.setAttributes(map); throw e; }
public abstract class X10Doc implements Doc { private String rawComment; private String comment; X10Tag[] firstSentenceTags, inlineTags; List<Tag> blockTags = new ArrayList<Tag>(); List<Tag> paramTags = new ArrayList<Tag>(); public void processComment(String rawComment) { this.rawComment = rawComment; procComment(rawCommentToText(rawComment)); } /** * Initializes fields comment, inlineTags of the object * * @param commentText the processed comment text */ private void procComment(String commentText) { // initialize inlineTags ArrayList<Tag> result = new ArrayList<Tag>(); String noInlineTags = replaceAtSigns(commentText); /* * Pattern p = Pattern.compile("\\{\\s*@[^}]*\\}"); // matches inline * tags // Pattern p = * Pattern.compile("\\{\\s*@([^\\s\\}]+)\\s*([^\\}]*)\\s*}"); // matches * inline tags Matcher m = p.matcher(commentText); int start = 0, end = * 0; // create an array of tag objects of kind "Text" and "@link"; as * explained in the // doclet API, for a comment // This is a {@link Doc * commentlabel} example. // create an array of Tag objects: // * * tags[0] is a Tag with name "Text" and text consisting of "This is a " * // * tags[1] is a SeeTag with name "@link", and label "commentlabel" * // * tags[2] is a Tag with name "Text" and text consisting of * " example." while (m.find()) { end = m.start(); String linkText = * m.group(); // System.out.print("String = \"" + * commentText.substring(start, end)); // * System.out.println("\"; linkText = \"" + linkText + "\""); // * result.add(new X10Tag("Text", commentText.substring(start, end), * this)); result.add(X10Tag.processInlineTag(linkText, this)); //int * index = commentText.indexOf(linkText); //commentText = * commentText.substring(0, index) + commentText.substring(index + * linkText.length()); // result.add(new X10SeeTag(true, linkText, * this)); // "true" signifies an @link tag, as opposed to an @see tag * start = m.end(); } */ if (!commentText.startsWith("@")) { // make sure that there is a // beginning paragraph // initialize comment int blockTagStart = noInlineTags.indexOf("@"); // start of block // tags within // comment blockTagStart = (blockTagStart == -1) ? commentText.length() : blockTagStart; this.comment = commentText.substring(0, blockTagStart).trim(); if (!comment.equals("")) { result.addAll(createInlineTags(comment, this)); } // } // add constraints, if any // String decl = declString(); // if (decl != null) { // result.add(new X10Tag(decl, this)); // } // initialize firstSentenceTags BreakIterator b = BreakIterator.getSentenceInstance(); b.setText(comment); int start = 0; int end = 0; start = b.first(); end = b.next(); String firstSentence = ((start <= end) ? comment.substring(start, end).trim() : ""); // System.out.println("X10Doc.initializeFields(): firstSentence = \"" // + firstSentence + "\""); firstSentenceTags = createInlineTags(firstSentence, this).toArray(new X10Tag[0]); } else { firstSentenceTags = new X10Tag[0]; } inlineTags = result.toArray(new X10Tag[0]); // TODO: creating Tag objects for block tags and storing them in a field // of this object Pattern blockTagPattern = Pattern.compile("\\s*@[^@]*"); Matcher blockTagMatcher = blockTagPattern.matcher(noInlineTags); while (blockTagMatcher.find()) { String tagText = blockTagMatcher.group(); int start = blockTagMatcher.start(); processBlockTag(commentText.substring(start, start + tagText.length())); } } // replaces @ signs of inline tags in the comment with another char ("A") private String replaceAtSigns(String comment) { String result = comment; Pattern p = Pattern.compile("\\{\\s*@[^}]*\\}"); // matches inline tags Matcher m = p.matcher(comment); while (m.find()) { String linkText = m.group(); int atIndex = linkText.indexOf("@") + m.start(); result = result.substring(0, atIndex) + "A" + result.substring(atIndex + 1); } return result; } private void processBlockTag(String tagText) { String kind = null; String text = null; Pattern p = Pattern.compile("@[^\\s]*"); Matcher m = p.matcher(tagText); if (m.find()) { kind = m.group().substring(1); text = tagText.substring(m.end()).trim(); } if (kind.equals(X10Tag.PARAM)) { Pattern p1 = Pattern.compile("[^\\s]*"); Matcher m1 = p1.matcher(text); if (m1.find()) { String name = m1.group(); String paramComment = text.substring(m1.end()).trim(); X10Tag[] inTags = createInlineTags(paramComment, this).toArray(new X10Tag[0]); X10ParamTag t = new X10ParamTag(false, name, inTags, paramComment, text, this); blockTags.add(t); paramTags.add(t); } } else if (kind.equals("see") || kind.equals("link") || kind.equals("linkplain")) { blockTags.add(new X10SeeTag("@" + kind, text, text, this)); } else if (kind.equals(X10Tag.THROWS)) { blockTags.add(new X10ThrowsTag(kind, text, this)); } else { blockTags.add(new X10Tag(kind, text, this)); } } public X10Tag[] getX10Tags() { return new X10Tag[0]; } public void addGuardTags(List<X10Tag> list) { StringBuilder sb = new StringBuilder(); Tag[] tags = tags(X10Tag.GUARD); if (tags.length > 0) { sb.append("<DL><DT><B>Guard:</B>"); for (Tag tag : tags) { sb.append("<DD><CODE>"); String code = tag.text(); String tokens[] = code.split("\\s"); if (tokens.length > 1) { sb.append(tokens[0]); sb.append("</CODE> - "); sb.append(code.replace(tokens[0], "").trim()); } else { sb.append("</CODE>"); } } sb.append("</DL><P>"); } list.addAll(createInlineTags(sb.toString(), this)); } // used to add comment lines displaying X10 class declarations, specifically // the class constraints in // the declarations; the declaration string argument contains @link tags to // entities in the constraint // TODO: update this.rawComment, this.comment appropriately public void addDeclTag(String declString) { if (declString == null) { return; } X10Tag[] declTags = createInlineTags(declString, this).toArray(new X10Tag[0]); // firstSentenceTags = concat(declTags, firstSentenceTags); // place declaration before the first sentence of the existing comment // but do not add // it to firstSentenceTags; this ensures that the declaration string is // not displayed // in tables such as the "Class Summary" table inlineTags = concat(declTags, inlineTags); // int len = inlineTags.length + declTags.length; // X10Tag[] newTags = new X10Tag[len]; // int i; // for (i = 0; i < inlineTags.length; i++) { // newTags[i] = inlineTags[i]; // } // for (int j = 0 ; i < len; i++, j++) { // newTags[i] = declTags[j]; // } // inlineTags = newTags; } public static X10Tag[] concat(X10Tag[] orig, X10Tag[] newTags) { int len = orig.length + newTags.length; X10Tag[] result = new X10Tag[len]; int i; for (i = 0; i < orig.length; i++) { result[i] = orig[i]; } for (int j = 0; i < len; i++, j++) { result[i] = newTags[j]; } return result; } // method to be overriden in sub-classes such as X10FieldDoc, X10MethodDoc, // X10ConstructorDoc, // X10TypeDefDoc, etc; the return string may be a description of the field // type or declaration of // the method etc., and is expected to be added to the comments of the // associated Doc object, by // a method such as X10Doc.addDeclTag(...) public String declString() { return ""; } // used to add a comment line displaying X10 type of a field, // method/constructor return value, // and method/constructor parameter, specifically the constraints in these // X10 types; argument str // cannot have any inline (or block) tags // TODO: will not work if comment contains block tags public void addNewLineToComment(String str) { if (str == null || str.equals("")) { return; } String newLine = "<PRE>\n</PRE>" + str; this.rawComment = this.rawComment + newLine; processComment(comment + newLine); } /** * Creates an array of Tag objects of kind "Text" and "@link" for the specified string. * * @param text processed comment text which may contain inline tags, but not block tags */ public static List<X10Tag> createInlineTags(String text, X10Doc holder) { ArrayList<X10Tag> result = new ArrayList<X10Tag>(); Pattern p = Pattern.compile("\\{\\s*@[^}]*\\}"); Matcher m = p.matcher(text); int start = 0, end = 0; while (m.find()) { end = m.start(); String linkText = m.group(); // System.out.print("String = \"" + text.substring(start, end)); // System.out.println("\"; linkText = \"" + linkText + "\""); result.add(new X10Tag("Text", text.substring(start, end), holder)); result.add(X10Tag.processInlineTag(linkText, holder)); // result.add(new X10Tag("Text", text.substring(start, end), this)); // result.add(new X10SeeTag(true, linkText, this)); // "true" signifies an @link tag, as opposed to an @see tag start = m.end(); } result.add(new X10Tag("Text", text.substring(start, text.length()), holder)); // System.out.println("Rest = \"" + text.substring(start, text.length()) // + "\""); if (result.isEmpty()) { result.add(new X10Tag("Text", text, holder)); } return result; } public String commentText() { if (X10RootDoc.printSwitch) System.out.println("Doc.commentText() called for " + name()); return comment; } public Tag[] firstSentenceTags() { if (X10RootDoc.printSwitch) System.out.println("Doc.firstSentenceTags() called for " + name()); return firstSentenceTags; } public String getRawCommentText() { if (X10RootDoc.printSwitch) System.out.println("Doc.getRawCommentText() called for " + name()); new Exception().printStackTrace(); return rawComment; } /** * Return comment as an array of tags. Includes inline tags (i.e. {@link reference} tags) but not * block tags. */ public Tag[] inlineTags() { if (X10RootDoc.printSwitch) System.out.println("Doc.inlineTags() called for " + name()); return inlineTags; } public boolean isAnnotationType() { return false; } public boolean isAnnotationTypeElement() { return false; } public boolean isClass() { return false; } public boolean isConstructor() { return false; } public boolean isEnum() { return false; } public boolean isEnumConstant() { return false; } public boolean isError() { return false; } public boolean isException() { return false; } public boolean isField() { return false; } public boolean isIncluded() { return false; } public boolean isInterface() { return false; } public boolean isMethod() { return false; } public boolean isOrdinaryClass() { return false; } public int compareTo(Object other) { return name().compareTo(((X10Doc) other).name()); } public String name() { if (X10RootDoc.printSwitch) System.out.println("Doc.name() called for " + this); return "Dummy Doc Name"; } public SourcePosition position() { if (X10RootDoc.printSwitch) System.out.println("Doc.position() called for " + name()); return null; } /** Return the see also tags in this Doc item. */ public SeeTag[] seeTags() { if (X10RootDoc.printSwitch) System.out.println("Doc.seeTags() called for " + name()); Tag[] tags = tags(X10Tag.SEE); SeeTag[] newTags = new SeeTag[tags.length]; System.arraycopy(tags, 0, newTags, 0, tags.length); return newTags; } public void setRawCommentText(String arg0) { if (X10RootDoc.printSwitch) System.out.println("Doc.setRawCommentText(String) called for " + name()); this.rawComment = arg0; processComment(rawComment); } /** Return all tags in this Doc item. */ public Tag[] tags() { if (X10RootDoc.printSwitch) System.out.println("Doc.tags() called for " + name()); Tag[] result = new Tag[blockTags.size() + inlineTags.length]; if (blockTags.size() > 0) System.arraycopy(blockTags.toArray(new Tag[0]), 0, result, 0, blockTags.size()); if (inlineTags.length > 0) System.arraycopy(inlineTags, 0, result, blockTags.size(), inlineTags.length); return result; // return inlineTags(); } /** Return tags of the specified kind in this Doc item. */ public Tag[] tags(String kind) { // TODO Auto-generated method stub if (X10RootDoc.printSwitch) System.out.println("Doc.tags(" + kind + ") called for " + name()); if (kind.equals("Text")) { return inlineTags(); } else return getTags(kind); } private Tag[] getTags(String kind) { List<Tag> result = new ArrayList<Tag>(); for (Tag t : blockTags) { if (t.kind().equals(kind)) { result.add(t); } } return result.toArray(new X10Tag[0]); } public static boolean isIncluded(String accessModFilter, ProgramElementDoc pd) { boolean isPublic = pd.isPublic(); if (accessModFilter.equals("-public")) { return isPublic; } boolean isProtected = pd.isProtected(); if (accessModFilter.equals("-protected")) { return (isPublic || isProtected); } boolean isPackage = pd.isPackagePrivate(); if (accessModFilter.equals("-package")) { return (isPublic || isProtected || isPackage); } return true; } public static String rawCommentToText(String rawComment) { if (rawComment == null || rawComment.length() == 0) { return ""; } assert (rawComment.startsWith("/**")) : "Non-javadoc comment: " + rawComment; assert (rawComment.endsWith("*/")) : "No comment terminator: " + rawComment; String result = rawComment; result = result.replaceFirst("^/\\*\\*\\s*", ""); result = result.replaceFirst("\\s*\\*/$", ""); result = result.replaceAll("\\n*\\s*(\\*)+\\s?", "\n"); return result.trim(); } private static final Map<String, Integer> flagsToHex = CollectionFactory.newHashMap(); static { flagsToHex.put(Flags.PUBLIC.toString(), 0x0001); flagsToHex.put(Flags.PRIVATE.toString(), 0x0002); flagsToHex.put(Flags.PROTECTED.toString(), 0x0004); flagsToHex.put(Flags.STATIC.toString(), 0x0008); flagsToHex.put(Flags.FINAL.toString(), 0x0010); // flagsToHex.put(Flags.SYNCHRONIZED.toString(), 0x0020); flagsToHex.put(Flags.NATIVE.toString(), 0x0100); flagsToHex.put(Flags.ABSTRACT.toString(), 0x0400); // flagsToHex.put(Flags.STRICTFP.toString(), 0x0800); } public static int flagsToModifierSpecifier(Set<String> flags) { int r = 0; for (String flag : flags) { // flag could be "property" which is not in flagsToHex (and not // recognized by // the standard doclet) if (flagsToHex.containsKey(flag)) { r |= flagsToHex.get(flag); } } return r; } }
/** * ClassFile represents a Java classfile as it is found on disk. The classfile is modelled according * to the Java Virtual Machine Specification. Methods are provided to access the classfile at a very * low level. * * @see polyglot.types.reflect Attribute * @see polyglot.types.reflect ConstantValue * @see polyglot.types.reflect Field * @see polyglot.types.reflect Method * @author Nate Nystrom */ public class ClassFile { protected Constant[] constants; // The constant pool protected int modifiers; // This class's modifier bit field protected int thisClass; protected int superClass; protected int[] interfaces; protected Field[] fields; protected Method[] methods; protected Attribute[] attrs; protected InnerClasses innerClasses; protected InnerClasses.Info innerClassInfo; protected Signature signature; protected File classFileSource; protected ExtensionInfo extensionInfo; protected Map<String, JLCInfo> jlcInfoCache = CollectionFactory.newHashMap(); /** * Constructor. This constructor parses the class file from the byte array * * @param code A byte array containing the class data */ public ClassFile(File classFileSource, byte[] code, ExtensionInfo ext) { this.classFileSource = classFileSource; this.extensionInfo = ext; try { ByteArrayInputStream bin = new ByteArrayInputStream(code); DataInputStream in = new DataInputStream(bin); read(in); in.close(); bin.close(); } catch (IOException e) { throw new InternalCompilerError("I/O exception on ByteArrayInputStream"); } } JLCInfo getJLCInfo(String typeSystemKey) { // Check if already set. JLCInfo jlc = (JLCInfo) jlcInfoCache.get(typeSystemKey); if (jlc != null) { return jlc; } jlc = new JLCInfo(); jlcInfoCache.put(typeSystemKey, jlc); try { int mask = 0; for (int i = 0; i < fields.length; i++) { if (fields[i].name().equals("jlc$SourceLastModified$" + typeSystemKey)) { jlc.sourceLastModified = fields[i].getLong(); mask |= 1; } else if (fields[i].name().equals("jlc$CompilerVersion$" + typeSystemKey)) { jlc.compilerVersion = fields[i].getString(); mask |= 2; } else if (fields[i].name().equals("jlc$ClassType$" + typeSystemKey)) { // there is encoded class type information. StringBuffer encodedClassTypeInfo = new StringBuffer(fields[i].getString()); // check to see if there are more fields. int seeking = 1; boolean found; do { found = false; String suffix = ("$" + seeking); String seekingFieldName = "jlc$ClassType$" + typeSystemKey + suffix; for (int j = 0; j < fields.length; j++) { if (fields[j].name().equals(seekingFieldName)) { encodedClassTypeInfo.append(fields[j].getString()); found = true; seeking++; break; } } } while (found); jlc.encodedClassType = encodedClassTypeInfo.toString(); mask |= 4; } } if (mask != 7) { // Not all the information is there. Reset to default. jlc.sourceLastModified = 0; jlc.compilerVersion = null; jlc.encodedClassType = null; } } catch (SemanticException e) { jlc.sourceLastModified = 0; jlc.compilerVersion = null; jlc.encodedClassType = null; } return jlc; } /** Get the encoded source modified time. */ public long sourceLastModified(String ts) { JLCInfo jlc = getJLCInfo(ts); return jlc.sourceLastModified; } /** Get the encoded compiler version used to compile the source. */ public String compilerVersion(String ts) { JLCInfo jlc = getJLCInfo(ts); return jlc.compilerVersion; } /** Get the encoded class type for the given type system. */ public String encodedClassType(String typeSystemKey) { JLCInfo jlc = getJLCInfo(typeSystemKey); return jlc.encodedClassType; } /** Read the class file. */ void read(DataInputStream in) throws IOException { // Read in file contents from stream readHeader(in); readConstantPool(in); readAccessFlags(in); readClassInfo(in); readFields(in); readMethods(in); readAttributes(in); } /** Get the class name at the given constant pool index. */ public String classNameCP(int index) { Constant c = constants[index]; if (c != null && c.tag() == Constant.CLASS) { Integer nameIndex = (Integer) c.value(); if (nameIndex != null) { return className(nameIndex.intValue()); } } return null; } public String className(int idx) { Constant c; c = constants[idx]; if (c.tag() == Constant.UTF8) { String s = (String) c.value(); return s.replace('/', '.'); } return null; } /** * Get the name of the class, including the package name. * * @return The name of the class. */ public String name() { Constant c = constants[thisClass]; if (c.tag() == Constant.CLASS) { Integer nameIndex = (Integer) c.value(); if (nameIndex != null) { c = constants[nameIndex.intValue()]; if (c.tag() == Constant.UTF8) { return (String) c.value(); } } } throw new ClassFormatError("Couldn't find class name in file"); } /** * Get the source of the class. * * @return The source of the class. */ public File classFileSource() { return this.classFileSource; } /** * Read a constant from the constant pool. * * @param in The stream from which to read. * @return The constant. * @exception IOException If an error occurs while reading. */ Constant readConstant(DataInputStream in) throws IOException { int tag = in.readUnsignedByte(); Object value; switch (tag) { case Constant.CLASS: case Constant.STRING: case Constant.METHOD_TYPE: // @since 1.8 value = Integer.valueOf(in.readUnsignedShort()); break; case Constant.FIELD_REF: case Constant.METHOD_REF: case Constant.INTERFACE_METHOD_REF: case Constant.NAME_AND_TYPE: case Constant.INVOKE_DYNAMIC: // @since 1.8 value = new int[2]; ((int[]) value)[0] = in.readUnsignedShort(); ((int[]) value)[1] = in.readUnsignedShort(); break; case Constant.INTEGER: value = Integer.valueOf(in.readInt()); break; case Constant.FLOAT: value = Float.valueOf(in.readFloat()); break; case Constant.LONG: // Longs take up 2 constant pool entries. value = Long.valueOf(in.readLong()); break; case Constant.DOUBLE: // Doubles take up 2 constant pool entries. value = Double.valueOf(in.readDouble()); break; case Constant.UTF8: value = in.readUTF(); break; case Constant.METHOD_HANDLE: // @since 1.8 value = new int[2]; ((int[]) value)[0] = in.readUnsignedByte(); ((int[]) value)[1] = in.readUnsignedShort(); break; default: throw new ClassFormatError("Invalid constant tag: " + tag); } return new Constant(tag, value); } /** * Read the class file header. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ void readHeader(DataInputStream in) throws IOException { int magic = in.readInt(); if (magic != 0xCAFEBABE) { throw new ClassFormatError("Bad magic number."); } int major = in.readUnsignedShort(); int minor = in.readUnsignedShort(); } /** * Read the class's constant pool. Constants in the constant pool are modeled by an array of * <tt>reflect.Constant</tt>/ * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. * @see Constant * @see #constants */ void readConstantPool(DataInputStream in) throws IOException { int count = in.readUnsignedShort(); constants = new Constant[count]; // The first constant is reserved for internal use by the JVM. constants[0] = null; // Read the constants. for (int i = 1; i < count; i++) { constants[i] = readConstant(in); switch (constants[i].tag()) { case Constant.LONG: case Constant.DOUBLE: // Longs and doubles take up 2 constant pool entries. constants[++i] = null; break; } } } /** * Read the class's access flags. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ void readAccessFlags(DataInputStream in) throws IOException { modifiers = in.readUnsignedShort(); } /** * Read the class's name, superclass, and interfaces. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ void readClassInfo(DataInputStream in) throws IOException { int index; thisClass = in.readUnsignedShort(); superClass = in.readUnsignedShort(); int numInterfaces = in.readUnsignedShort(); interfaces = new int[numInterfaces]; for (int i = 0; i < numInterfaces; i++) { interfaces[i] = in.readUnsignedShort(); } } /** * Read the class's fields. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ void readFields(DataInputStream in) throws IOException { int numFields = in.readUnsignedShort(); fields = new Field[numFields]; for (int i = 0; i < numFields; i++) { fields[i] = createField(in); } } /** * Read the class's methods. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ void readMethods(DataInputStream in) throws IOException { int numMethods = in.readUnsignedShort(); methods = new Method[numMethods]; for (int i = 0; i < numMethods; i++) { methods[i] = createMethod(in); } } /** * Read the class's attributes. Since none of the attributes are required, just read the length of * each attribute and skip that many bytes. * * @param in The stream from which to read. * @exception IOException If an error occurs while reading. */ public void readAttributes(DataInputStream in) throws IOException { int numAttributes = in.readUnsignedShort(); attrs = new Attribute[numAttributes]; for (int i = 0; i < numAttributes; i++) { int nameIndex = in.readUnsignedShort(); int length = in.readInt(); String name = (String) constants[nameIndex].value(); Attribute a = createAttribute(in, name, nameIndex, length); if (a != null) { attrs[i] = a; } else { long n = in.skip(length); if (n != length) { throw new EOFException(); } } } } public Method createMethod(DataInputStream in) throws IOException { Method m = new Method(in, this); m.initialize(); return m; } public Field createField(DataInputStream in) throws IOException { Field f = new Field(in, this); f.initialize(); return f; } public Attribute createAttribute(DataInputStream in, String name, int nameIndex, int length) throws IOException { if (name.equals("InnerClasses")) { innerClasses = new InnerClasses(in, nameIndex, length); Info[] allInnerInfos = innerClasses.getClasses(); for (int i = 0; i < allInnerInfos.length; i++) { if (allInnerInfos[i].classIndex == getThisClass()) { innerClassInfo = allInnerInfos[i]; } } return innerClasses; } if (name.equals("Signature")) { signature = new Signature(this, in, nameIndex, length); return signature; } return null; } public Attribute[] getAttrs() { return attrs; } public Constant[] getConstants() { return constants; } public Field[] getFields() { return fields; } public InnerClasses getInnerClasses() { return innerClasses; } public InnerClasses.Info getInnerClassInfo() { return innerClassInfo; } public int[] getInterfaces() { return interfaces; } public Method[] getMethods() { return methods; } public int getModifiers() { if (innerClassInfo != null) return innerClassInfo.modifiers; return modifiers; } public int getSuperClass() { return superClass; } public int getThisClass() { return thisClass; } public Signature getSignature() { return signature; } public String signature() { if (this.signature != null) { return (String) constants[this.signature.getSignature()].value(); } return ""; } public String toString() { return Modifier.toString(modifiers) + "(" + Integer.toHexString(modifiers) + ") " + name() + signature(); } }