/* * Copyright (c) 1996, 2014, 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. * * 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 org.openjdk.asmtools.jasm; import static org.openjdk.asmtools.jasm.TypeAnnotationUtils.*; import static org.openjdk.asmtools.jasm.JasmTokens.*; import static org.openjdk.asmtools.jasm.ConstantPool.*; import static org.openjdk.asmtools.jasm.Tables.*; import java.io.IOException; import java.util.ArrayList; import java.util.TreeMap; /** * ParserAnnotation * * ParserAnnotation is a parser class owned by Parser.java. It is primarily responsible * for parsing Annotations (for classes, methods or fields). * * ParserAnnotation can parse the different types of Annotation Attributes: * Runtime(In)Visible Annotations (JDK 6+) Default Annotations (JDK 6+) * Runtime(In)VisibleParameter Annotations (JDK 7+) Runtime(In)VisibleType Annotations * (JSR308, JDK8+) */ public class ParserAnnotation extends ParseBase { /*-------------------------------------------------------- */ /* Annotation Inner Classes */ /** * AnnotationElemValue * * Used to store Annotation values */ class AnnotationElemValue implements Data { AnnotationData annotation; public AnnotationElemValue(AnnotationData annotation) { this.annotation = annotation; } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeByte('@'); annotation.write(out); } @Override public int getLength() { return 1 + annotation.getLength(); } } /** * ClassElemValue * * Annotation Element value referring to a class */ class ClassElemValue implements Data { ConstCell indx; public ClassElemValue(ConstCell indx) { this.indx = indx; } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeByte('c'); indx.write(out); } @Override public int getLength() { return 3; } } /** * ArrayElemValue * * Annotation Element value referring to an Array */ class ArrayElemValue implements Data { ArrayList elemValues; int arrayLength = 0; public ArrayElemValue() { this.elemValues = new ArrayList<>(); } void add(Data elemValue) { elemValues.add(elemValue); arrayLength += elemValue.getLength(); } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeByte('['); out.writeShort(elemValues.size()); for (Data eval : elemValues) { eval.write(out); } } @Override public int getLength() { return 3 + arrayLength; } } /** * ConstElemValue * * Annotation Element value referring to a Constant */ class ConstElemValue implements Data { char tag; ConstCell indx; public ConstElemValue(char tag, ConstCell indx) { this.tag = tag; this.indx = indx; } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeByte(tag); indx.write(out); } @Override public int getLength() { return 3; } } /** * EnumElemValue * * Element Value for Enums */ class EnumElemValue implements Data { ConstCell type; ConstCell value; public EnumElemValue(ConstCell type, ConstCell value) { this.type = type; this.value = value; } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeByte('e'); type.write(out); value.write(out); } @Override public int getLength() { return 5; } } /*-------------------------------------------------------- */ /* Annotation Parser Fields */ /** * local handles on the scanner, main parser, and the error reporting env */ private static TTVis ttVisitor; /*-------------------------------------------------------- */ /** * main constructor * * @param scanner * @param parser * @param env */ protected ParserAnnotation(Scanner scanner, Parser parser, Environment env) { super.init(scanner, parser, env); ttVisitor = new TTVis(); ttVisitor.init(env, scanner); } protected void scanParamName(int totalParams, int paramNum, MethodData curMethod) throws IOException { debugScan(" - - - > [ParserAnnotation.scanParamName]: Begin "); scanner.scan(); scanner.expect(Token.LBRACE); // First scan the Name (String, or CPX to name) ConstCell nameCell = null; if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) { // Got a Class Name nameCell = parser.parseName(); } else if (scanner.token == Token.CPINDEX) { int cpx = scanner.intValue; nameCell = parser.pool.getCell(cpx); // check the constant ConstValue nameCellValue = nameCell.ref; if (!(nameCellValue instanceof ConstValue_String)) { // throw an error env.error(scanner.pos, "paramname.constnum.invaltype", cpx); throw new Scanner.SyntaxError(); } } else { // throw scan error - unexpected token env.error(scanner.pos, "paramname.token.unexpected", scanner.stringValue); throw new Scanner.SyntaxError(); } // Got the name cell. Next, scan the access flags int mod = parser.scanModifiers(); scanner.expect(Token.RBRACE); curMethod.addMethodParameter(totalParams, paramNum, nameCell, mod); debugScan(" - - - > [ParserAnnotation.scanParamName]: End "); } /** * scanAnnotations * * The main entry for parsing an annotation list. * * @return An ArrayList of parsed annotations * @throws IOException */ protected ArrayList scanAnnotations() throws IOException { ArrayList annttns = new ArrayList<>(); while (scanner.token == Token.ANNOTATION) { if (isAnnotationToken(scanner.stringValue)) { annttns.add(parseAnnotation()); } else if (isTypeAnnotationToken(scanner.stringValue)) { annttns.add(parseTypeAnnotation()); } } if (annttns.size() > 0) { return annttns; } else { return null; } } /** * isAnnotation * * examines the beginning of a string to see if it starts with an annotation character * * @param str * @return True if the string starts with an annotation char. */ protected boolean isAnnotation(String str) { return (str.startsWith("@")); } /** * isAnnotationToken * * examines the beginning of a string to see if it starts with an annotation * characters ('@+' = visible annotation, '@-' = invisible). * * @param str * @return True if the string starts with an annotation char. */ protected boolean isAnnotationToken(String str) { return (str.startsWith("@+") || str.startsWith("@-")); } /** * isTypeAnnotationToken * * examines the beginning of a string to see if it starts with type annotation * characters ('@T+' = visible type annotation, '@T-' = invisible). * * @param str * @return True if the string starts with an annotation char. */ protected boolean isTypeAnnotationToken(String str) { return (str.startsWith("@T+") || str.startsWith("@T-")); } /** * isInvisibleAnnotToken * * examines the end of an annotation token to determine visibility ('+' = visible * annotation, '-' = invisible). * * @param str * @return True if the token implies invisible annotation. */ protected boolean isInvisibleAnnotToken(String str) { return (str.endsWith("-")); } /** * parseDefaultAnnotation * * parses a default Annotation attribute * * @return the parsed Annotation Attribute * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError * @throws IOException */ protected DefaultAnnotationAttr parseDefaultAnnotation() throws Scanner.SyntaxError, IOException { scanner.scan(); DefaultAnnotationAttr attr = null; Data value = null; scanner.expect(Token.LBRACE); if ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { value = scanAnnotationData("default"); } scanner.expect(Token.RBRACE); attr = new DefaultAnnotationAttr(parser.cd, AttrTag.ATT_AnnotationDefault.parsekey(), value); return attr; } /** * parseParamAnnots * * Parses Parameter Annotations attributes. * * @param _totalParams * @param curMethod * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError * @throws IOException */ protected void parseParamAnnots(int _totalParams, MethodData curMethod) throws Scanner.SyntaxError, IOException { debugScan(" - - - > [ParserAnnotation.parseParamAnnots]: Begin, totalParams = " + _totalParams + " "); // _The method thinks there are N+1 params in the signature // (N = total params in the call list) + 1 (return value) int totalParams = _totalParams - 1; TreeMap> pAnnots = new TreeMap<>(); while (scanner.token == Token.INTVAL) { // Create the Parameter Array for Param Annotations // Do something with Parameter annotations // -------------------- // First - validate that the parameter number (integer) // (eg >= 0, < numParams, and param num is not previously set) int paramNum = scanner.intValue; Integer iParamNum = new Integer(paramNum); if (paramNum < 0 || paramNum >= totalParams) { //invalid Parameter number. Throw an error. env.error(scanner.pos, "invalid.paramnum", paramNum); } if (pAnnots.get(iParamNum) != null) { // paramter is already populated with annotations/pnames, Throw an error. env.error(scanner.pos, "duplicate.paramnum", paramNum); } // 2nd - Parse the COLON (invalid if not present) scanner.scan(); scanner.expect(Token.COLON); // 3rd - parse either an optional ParamName, or a list of annotations if (scanner.token == Token.PARAM_NAME) { //parse the ParamName scanParamName(_totalParams, iParamNum, curMethod); } // 4th - parse each Annotation (followed by comma, followed by annotation // assign array of annotations to param array if (scanner.token == Token.ANNOTATION) { ArrayList pAnnot = scanAnnotations(); pAnnots.put(iParamNum, pAnnot); for (AnnotationData data : pAnnot) { curMethod.addParamAnnotation(totalParams, paramNum, data); } } } } /* ************************* Private Members *************************** */ /** * parseTypeAnnotation * * parses an individual annotation. * * @return a parsed annotation. * @throws IOException */ private AnnotationData parseTypeAnnotation() throws Scanner.SyntaxError, IOException { boolean invisible = isInvisibleAnnotToken(scanner.stringValue); scanner.scan(); debugScan(" [ParserAnnotation.parseTypeAnnotation]: id = " + scanner.stringValue + " "); String annoName = "L" + scanner.stringValue + ";"; TypeAnnotationData anno = new TypeAnnotationData(parser.pool.FindCellAsciz(annoName), invisible); scanner.scan(); debugScan(" [ParserAnnotation.parseTypeAnnotation]:new type annotation: " + annoName + " "); scanner.expect(Token.LBRACE); // Scan the usual annotation data _scanAnnotation(anno); // scan the Target _scanTypeTarget(anno); // scan the Location _scanTargetPath(anno); scanner.expect(Token.RBRACE); return anno; } /** * scanAnnotation * * parses an individual annotation. * * @return a parsed annotation. * @throws IOException */ private AnnotationData parseAnnotation() throws Scanner.SyntaxError, IOException { debugScan(" - - - > [ParserAnnotation.parseAnnotation]: Begin "); boolean invisible = isInvisibleAnnotToken(scanner.stringValue); scanner.scan(); String annoName = "L" + scanner.stringValue + ";"; AnnotationData anno = new AnnotationData(parser.pool.FindCellAsciz(annoName), invisible); scanner.scan(); debugScan("[ParserAnnotation.parseAnnotation]: new annotation: " + annoName); _scanAnnotation(anno); return anno; } /** * _scanAnnotation * * parses an individual annotation-data. * * @return a parsed annotation. * @throws IOException */ private void _scanAnnotation(AnnotationData annotData) throws Scanner.SyntaxError, IOException { debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Begin"); scanner.expect(Token.LBRACE); while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { ConstCell nameCell = parser.parseName(); scanner.expect(Token.ASSIGN); ConstValue cellref = nameCell.ref; if (cellref.tag != ConstType.CONSTANT_UTF8) { throw new Scanner.SyntaxError(); } String name = ((ConstValue_String) cellref)._toString(); debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Annot - Field Name: " + name); Data data = scanAnnotationData(name); annotData.add(new AnnotationData.ElemValuePair(nameCell, data)); // consume tokens inbetween annotation fields if (scanner.token == Token.COMMA) { scanner.scan(); } } scanner.expect(Token.RBRACE); } /** * _scanAnnotation * * parses an individual annotation-data. * * @return a parsed annotation. * @throws IOException */ private void _scanTypeTarget(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { debugScan(" [ParserAnnotation._scanTypeTarget]: Begin "); scanner.expect(Token.LBRACE); //Scan the target_type and the target_info scanner.expect(Token.IDENT); debugScan(" [ParserAnnotation._scanTypeTarget]: TargetType: " + scanner.idValue); TypeAnnotationUtils.TargetType tt = TypeAnnotationUtils.getTargetType(scanner.idValue); if (tt == null) { env.error(scanner.pos, "incorrect.typeannot.target", scanner.idValue); throw new Scanner.SyntaxError(); } debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetType: " + tt); if (ttVisitor.scanner == null) { ttVisitor.scanner = scanner; } ttVisitor.visitExcept(tt); annotData.targetInfo = ttVisitor.getTargetInfo(); annotData.targetType = tt; debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetInfo: " + annotData.targetInfo); scanner.expect(Token.RBRACE); } /** * TTVis * * Target Type visitor, used for constructing the target-info within a type * annotation. visitExcept() is the entry point. ti is the constructed target info. */ private static class TTVis extends TypeAnnotationUtils.TypeAnnotationTargetVisitor { private TypeAnnotationUtils.TargetInfo ti; private IOException IOProb; private Scanner.SyntaxError SyProb; private Scanner scanner; private Environment env; public TTVis() { super(); reset(); } public void init(Environment en, Scanner scn) { if (scanner == null) { scanner = scn; } if (env == null) { env = en; } } public final void reset() { ti = null; IOProb = null; SyProb = null; } //This is the entry point for a visitor that tunnels exceptions public void visitExcept(TypeAnnotationUtils.TargetType tt) throws IOException, Scanner.SyntaxError { IOProb = null; SyProb = null; ti = null; visit(tt); if (IOProb != null) { throw IOProb; } if (SyProb != null) { throw SyProb; } } public TypeAnnotationUtils.TargetInfo getTargetInfo() { return ti; } // this fn gathers intvals, and tunnels any exceptions thrown by // the scanner private int scanIntVal(TypeAnnotationUtils.TargetType tt) { int ret = -1; if (scanner.token == Token.INTVAL) { ret = scanner.intValue; try { scanner.scan(); } catch (IOException e) { IOProb = e; } catch (Scanner.SyntaxError e) { SyProb = e; } } else { env.error(scanner.pos, "incorrect.typeannot.targtype.int", tt.parseKey(), scanner.token); SyProb = new Scanner.SyntaxError(); } return ret; } // this fn gathers intvals, and tunnels any exceptions thrown by // the scanner private String scanStringVal(TypeAnnotationUtils.TargetType tt) { String ret = ""; if (scanner.token == Token.STRINGVAL) { ret = scanner.stringValue; try { scanner.scan(); } catch (IOException e) { IOProb = e; } catch (Scanner.SyntaxError e) { SyProb = e; } } else { env.error(scanner.pos, "incorrect.typeannot.targtype.string", tt.parseKey(), scanner.token); SyProb = new Scanner.SyntaxError(); } return ret; } // this fn gathers intvals, and tunnels any exceptions thrown by // the scanner private void scanBrace(boolean left) { try { scanner.expect(left ? Token.LBRACE : Token.RBRACE); } catch (IOException e) { IOProb = e; } catch (Scanner.SyntaxError e) { SyProb = e; } } private boolean error() { return IOProb != null || SyProb != null; } @Override public void visit_type_param_target(TypeAnnotationUtils.TargetType tt) { env.traceln("Type Param Target: "); int byteval = scanIntVal(tt); // param index if (!error()) { ti = new TypeAnnotationUtils.typeparam_target(tt, byteval); } } @Override public void visit_supertype_target(TypeAnnotationUtils.TargetType tt) { env.traceln("SuperType Target: "); int shortval = scanIntVal(tt); // type index if (!error()) { ti = new TypeAnnotationUtils.supertype_target(tt, shortval); } } @Override public void visit_typeparam_bound_target(TypeAnnotationUtils.TargetType tt) { env.traceln("TypeParam Bound Target: "); int byteval1 = scanIntVal(tt); // param index if (error()) { return; } int byteval2 = scanIntVal(tt); // bound index if (error()) { return; } ti = new TypeAnnotationUtils.typeparam_bound_target(tt, byteval1, byteval2); } @Override public void visit_empty_target(TypeAnnotationUtils.TargetType tt) { env.traceln("Empty Target: "); if (!error()) { ti = new TypeAnnotationUtils.empty_target(tt); } } @Override public void visit_methodformalparam_target(TypeAnnotationUtils.TargetType tt) { env.traceln("MethodParam Target: "); int byteval = scanIntVal(tt); // param index if (!error()) { ti = new TypeAnnotationUtils.methodformalparam_target(tt, byteval); } } @Override public void visit_throws_target(TypeAnnotationUtils.TargetType tt) { env.traceln("Throws Target: "); int shortval = scanIntVal(tt); // exception index if (!error()) { ti = new TypeAnnotationUtils.throws_target(tt, shortval); } } @Override public void visit_localvar_target(TypeAnnotationUtils.TargetType tt) { env.traceln("LocalVar Target: "); TypeAnnotationUtils.localvar_target locvartab = new TypeAnnotationUtils.localvar_target(tt, 0); ti = locvartab; while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { // consume the left brace scanBrace(true); if (error()) { return; } // scan the local var triple int shortval1 = scanIntVal(tt); // startPC if (error()) { return; } int shortval2 = scanIntVal(tt); // length if (error()) { return; } int shortval3 = scanIntVal(tt); // CPX locvartab.addEntry(shortval1, shortval2, shortval3); scanBrace(false); if (error()) { return; } } } @Override public void visit_catch_target(TypeAnnotationUtils.TargetType tt) { env.traceln("Catch Target: "); int shortval = scanIntVal(tt); // catch index ti = new TypeAnnotationUtils.catch_target(tt, shortval); } @Override public void visit_offset_target(TypeAnnotationUtils.TargetType tt) { env.traceln("Offset Target: "); int shortval = scanIntVal(tt); // offset index if (!error()) { ti = new TypeAnnotationUtils.offset_target(tt, shortval); } } @Override public void visit_typearg_target(TypeAnnotationUtils.TargetType tt) { env.traceln("TypeArg Target: "); int shortval = scanIntVal(tt); // offset if (error()) { return; } int byteval = scanIntVal(tt); // type index if (error()) { return; } ti = new TypeAnnotationUtils.typearg_target(tt, shortval, byteval); } } /** * _scanTypeLocation * * parses an individual annotation-data. * * @return a parsed annotation. * @throws IOException */ private void _scanTargetPath(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { ArrayList targPath = new ArrayList<>(); // parse the location info scanner.expect(Token.LBRACE); while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { TypePathEntry tpe = _scanTypePathEntry(); scanner.scan(); // throw away comma if (scanner.token == Token.COMMA) { scanner.scan(); } } scanner.expect(Token.RBRACE); annotData.targetPath = targPath; } /** * _scanTypeLocation * * parses an individual annotation-data. * * @return a parsed annotation. * @throws IOException */ private TypePathEntry _scanTypePathEntry() throws Scanner.SyntaxError, IOException { TypePathEntry tpe; if ((scanner.token != Token.EOF) && (scanner.token == Token.STRINGVAL)) { String pathEntryKey = scanner.stringValue; PathKind pk = pathKind(pathEntryKey); if (pk == null) { // unexpected Type Path env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.stringValue); throw new Scanner.SyntaxError(); } if (pk == PathKind.ITHARG_PARAMETERTYPE) { // // need to scan the index // Take the form: INNER_TYPE(#) scanner.expect(Token.LPAREN); int index = 0; if ((scanner.token != Token.EOF) && (scanner.token == Token.INTVAL)) { index = scanner.intValue; } else { // incorrect Arg index env.error(scanner.pos, "incorrect.typeannot.pathentry.argindex", scanner.token); throw new Scanner.SyntaxError(); } scanner.expect(Token.RPAREN); tpe = new TypePathEntry(pk.key(), (char) index); } else { tpe = new TypePathEntry(pk.key(), (char) 0); } } else { // unexpected Type Path env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.token); throw new Scanner.SyntaxError(); } return tpe; } /** * scanAnnotationArray * * Scans an Array of annotations. * * @param name Name of the annotation * @return Array Element * @throws IOException if scanning errors exist */ private ArrayElemValue scanAnnotationArray(String name) throws IOException { scanner.scan(); ArrayElemValue arrayElem = new ArrayElemValue(); while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { Data data = scanAnnotationData(name + " {}"); arrayElem.add(data); // consume tokens inbetween annotation fields if (scanner.token == Token.COMMA) { scanner.scan(); } } scanner.expect(Token.RBRACE); return arrayElem; } /** * scanAnnotationEnum * * Scans an annotation enum val. * * @param name Annotation Name * @return Constant element value for the Class Annotation. * @throws IOException */ private Data scanAnnotationClass(String name) throws IOException { Data constVal = null; // scan the next identifier. // if it is an Ident, consume it as the class name. scanner.scan(); switch (scanner.token) { case IDENT: env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); //need to encode the stringval as an (internal) descriptor. String desc = parser.encodeClassString(scanner.stringValue); // note: for annotations, a class field points to a string with the class descriptor. constVal = new ConstElemValue('c', parser.pool.FindCellAsciz(desc)); scanner.scan(); break; case CPINDEX: // could be a reference to a class name env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); Integer ConstNmCPX = new Integer(scanner.stringValue); constVal = new ClassElemValue(parser.pool.getCell(ConstNmCPX)); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.class", scanner.stringValue); throw new Scanner.SyntaxError(); } return constVal; } /** * scanAnnotationEnum * * Scans an annotation enum val. * * @param name Annotation Name * @return Enumeration Element Value * @throws IOException for scanning errors. */ private EnumElemValue scanAnnotationEnum(String name) throws IOException { scanner.scan(); EnumElemValue enumval = null; switch (scanner.token) { case IDENT: // could be a string identifying enum class and name String enumClassName = scanner.stringValue; scanner.scan(); // could be a string identifying enum class and name switch (scanner.token) { case IDENT: // could be a string identifying enum class and name String enumTypeName = scanner.stringValue; env.traceln("[AnnotationParser.scanAnnotationEnum]:: Constant Enum Field: " + name + " = " + enumClassName + " " + enumTypeName); String encodedClass = parser.encodeClassString(enumClassName); ConstElemValue classConst = new ConstElemValue('s', parser.pool.FindCellAsciz(encodedClass)); ConstElemValue typeConst = new ConstElemValue('s', parser.pool.FindCellAsciz(enumTypeName)); enumval = new EnumElemValue(classConst.indx, typeConst.indx); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.enum", scanner.stringValue); throw new Scanner.SyntaxError(); } break; case CPINDEX: Integer typeNmCPX = new Integer(scanner.stringValue); scanner.scan(); //need two indexes to form a proper enum switch (scanner.token) { case CPINDEX: Integer ConstNmCPX = new Integer(scanner.stringValue); env.traceln("[AnnotationParser.scanAnnotationEnum]:: Enumeration Field: " + name + " = #" + typeNmCPX + " #" + ConstNmCPX); enumval = new EnumElemValue(parser.pool.getCell(typeNmCPX), parser.pool.getCell(ConstNmCPX)); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.enum.cpx"); throw new Scanner.SyntaxError(); } break; } return enumval; } /** * scanAnnotationData * * parses the internals of an annotation. * * @param name Annotation Name * @return a Data data structure containing the annotation data. * @throws IOException for scanning errors. */ private Data scanAnnotationData(String name) throws IOException { Data data = null; switch (scanner.token) { // This handles the Annotation types (as normalized in the constant pool) // Some primitive types (Boolean, char, short, byte) are identified by a keyword. case INTVAL: env.traceln("[AnnotationParser.scanAnnotationData]:: Integer Field: " + name + " = " + scanner.intValue); data = new ConstElemValue('I', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, new Integer(scanner.intValue))); scanner.scan(); break; case DOUBLEVAL: env.traceln("[AnnotationParser.scanAnnotationData]:: Double Field: " + name + " = " + scanner.doubleValue); double dval = new Double(scanner.doubleValue); long ivdal = Double.doubleToLongBits(dval); Long val = new Long(ivdal); data = new ConstElemValue('D', parser.pool.FindCell(ConstType.CONSTANT_DOUBLE, val)); scanner.scan(); break; case FLOATVAL: env.traceln("[AnnotationParser.scanAnnotationData]:: Float Field: " + name + " = " + scanner.floatValue); float fval = new Float(scanner.floatValue); int ifval = Float.floatToIntBits(fval); Integer val1 = new Integer(ifval); data = new ConstElemValue('F', parser.pool.FindCell(ConstType.CONSTANT_FLOAT, val1)); scanner.scan(); break; case LONGVAL: env.traceln("[AnnotationParser.scanAnnotationData]:: Long Field: " + name + " = " + scanner.longValue); data = new ConstElemValue('J', parser.pool.FindCell(ConstType.CONSTANT_LONG, new Long(scanner.longValue))); scanner.scan(); break; case STRINGVAL: env.traceln("[AnnotationParser.scanAnnotationData]:: String Field: " + name + " = " + scanner.stringValue); data = new ConstElemValue('s', parser.pool.FindCellAsciz(scanner.stringValue)); scanner.scan(); break; case CLASS: env.traceln("[AnnotationParser.scanAnnotationData]:: Class) keyword: " + scanner.stringValue); data = scanAnnotationClass(name); break; case ENUM: // scan the next two identifiers (eg ident.ident), or 2 CPRefs. // if it is an Ident, use consume it as the class name. env.traceln("[AnnotationParser.scanAnnotationData]:: Enum) keyword: " + scanner.stringValue); data = scanAnnotationEnum(name); break; case IDENT: env.traceln("[AnnotationParser.scanAnnotationData]:: JASM Keyword: (annotation field name: " + name + ") keyword: " + scanner.stringValue); data = scanAnnotationIdent(scanner.stringValue, name); break; case ANNOTATION: env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Field: " + name + " = " + scanner.stringValue); data = new AnnotationElemValue(parseAnnotation()); break; case LBRACE: env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Array Field: " + name); data = scanAnnotationArray(name); break; default: env.error(scanner.pos, "incorrect.annot.token", scanner.token); throw new Scanner.SyntaxError(); } return data; } /** * scanAnnotationIdent * * parses the identifier of an annotation. * * @param ident Basic Type identifier * @param name Annotation Name * @return Basic Type Annotation data * @throws IOException if scanning errors occur */ private Data scanAnnotationIdent(String ident, String name) throws IOException { // Handle JASM annotation Keyword Identifiers Data data = null; BasicType type = basictype(ident); switch (type) { case T_BOOLEAN: // consume the keyword, get the value scanner.scan(); switch (scanner.token) { case INTVAL: // Handle Boolean value in integer form env.traceln("Boolean Field: " + name + " = " + scanner.intValue); Integer val = new Integer(scanner.intValue); if (val > 1 || val < 0) { env.traceln("Warning: Boolean Field: " + name + " value is not 0 or 1, value = " + scanner.intValue); } data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); scanner.scan(); break; case IDENT: // handle boolean value with true/false keywords int val1 = 0; switch (scanner.stringValue) { case "true": val1 = 1; break; case "false": val1 = 0; break; default: throw new IOException("Incorrect Annotation (boolean), expected true/false), got \"" + scanner.stringValue + "\"."); } env.traceln("Boolean Field: " + name + " = " + scanner.stringValue); data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, new Integer(val1))); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.bool", scanner.stringValue); throw new Scanner.SyntaxError(); } break; case T_BYTE: // consume the keyword, get the value scanner.scan(); switch (scanner.token) { case INTVAL: env.traceln("Byte Field: " + name + " = " + scanner.intValue); Integer val = new Integer(scanner.intValue); if (val > 0xFF) { env.traceln("Warning: Byte Field: " + name + " value is greater than 0xFF, value = " + scanner.intValue); } data = new ConstElemValue('B', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.byte", scanner.stringValue); throw new Scanner.SyntaxError(); } break; case T_CHAR: // consume the keyword, get the value scanner.scan(); switch (scanner.token) { case INTVAL: env.traceln("Char Field: " + name + " = " + scanner.intValue); Integer val = new Integer(scanner.intValue); // Bounds check? data = new ConstElemValue('C', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.char", scanner.stringValue); throw new Scanner.SyntaxError(); } break; case T_SHORT: // consume the keyword, get the value scanner.scan(); switch (scanner.token) { case INTVAL: env.traceln("Short Field: " + name + " = " + scanner.intValue); Integer val = new Integer(scanner.intValue); if (val > 0xFFFF) { env.traceln("Warning: Short Field: " + name + " value is greater than 0xFFFF, value = " + scanner.intValue); } data = new ConstElemValue('S', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); scanner.scan(); break; default: env.error(scanner.pos, "incorrect.annot.short", scanner.stringValue); throw new Scanner.SyntaxError(); } break; default: env.error(scanner.pos, "incorrect.annot.keyword", ident); throw new Scanner.SyntaxError(); } return data; } }