org.aspectj.weaver.bcel.Utility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* PARC initial implementation
* ******************************************************************/
package org.aspectj.weaver.bcel;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Unknown;
import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
import org.aspectj.apache.bcel.generic.ArrayType;
import org.aspectj.apache.bcel.generic.BasicType;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionByte;
import org.aspectj.apache.bcel.generic.InstructionCP;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.InstructionSelect;
import org.aspectj.apache.bcel.generic.InstructionShort;
import org.aspectj.apache.bcel.generic.InstructionTargeter;
import org.aspectj.apache.bcel.generic.LineNumberTag;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.ReferenceType;
import org.aspectj.apache.bcel.generic.SwitchBuilder;
import org.aspectj.apache.bcel.generic.TargetLostException;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ConstantPoolReader;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.Lint;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.Utils;
import org.aspectj.weaver.World;
public class Utility {
private final static char PACKAGE_INITIAL_CHAR = AjAttribute.AttributePrefix.charAt(0);
public static List readAjAttributes(String classname, Attribute[] as, ISourceContext context, World w,
AjAttribute.WeaverVersionInfo version, ConstantPoolReader dataDecompressor) {
List attributes = new ArrayList<>();
// first pass, look for version
List forSecondPass = new ArrayList<>();
for (int i = as.length - 1; i >= 0; i--) {
Attribute a = as[i];
if (a instanceof Unknown) {
Unknown u = (Unknown) a;
String name = u.getName();
if (name.charAt(0) == PACKAGE_INITIAL_CHAR) { // 'o'rg.aspectj
if (name.startsWith(AjAttribute.AttributePrefix)) {
if (name.endsWith(WeaverVersionInfo.AttributeName)) {
version = (AjAttribute.WeaverVersionInfo) AjAttribute.read(version, name, u.getBytes(), context, w,
dataDecompressor);
if (version.getMajorVersion() > WeaverVersionInfo.getCurrentWeaverMajorVersion()) {
throw new BCException(
"Unable to continue, this version of AspectJ supports classes built with weaver version "
+ WeaverVersionInfo.toCurrentVersionString() + " but the class " + classname
+ " is version " + version.toString() + ". Please update your AspectJ.");
}
}
forSecondPass.add(u);
}
}
}
}
// FIXASC why going backwards? is it important
for (int i = forSecondPass.size() - 1; i >= 0; i--) {
Unknown a = forSecondPass.get(i);
String name = a.getName();
AjAttribute attr = AjAttribute.read(version, name, a.getBytes(), context, w, dataDecompressor);
if (attr != null) {
attributes.add(attr);
}
}
return attributes;
}
/*
* Ensure we report a nice source location - particular in the case where the source info is missing (binary weave).
*/
public static String beautifyLocation(ISourceLocation isl) {
StringBuilder nice = new StringBuilder();
if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().contains("no debug info available")) {
nice.append("no debug info available");
} else {
// can't use File.getName() as this fails when a Linux box
// encounters a path created on Windows and vice-versa
int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/');
if (takeFrom == -1) {
takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\');
}
nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1));
if (isl.getLine() != 0) {
nice.append(":").append(isl.getLine());
}
}
return nice.toString();
}
public static Instruction createSuperInvoke(InstructionFactory fact, BcelWorld world, Member signature) {
short kind;
if (Modifier.isInterface(signature.getModifiers())) {
throw new RuntimeException("bad");
} else if (Modifier.isPrivate(signature.getModifiers()) || signature.getName().equals("")) {
throw new RuntimeException("unimplemented, possibly bad");
} else if (Modifier.isStatic(signature.getModifiers())) {
throw new RuntimeException("bad");
} else {
kind = Constants.INVOKESPECIAL;
}
return fact.createInvoke(signature.getDeclaringType().getName(), signature.getName(),
BcelWorld.makeBcelType(signature.getReturnType()), BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind);
}
public static Instruction createInvoke(InstructionFactory fact, BcelWorld world, Member signature) {
short kind;
int signatureModifiers = signature.getModifiers();
if (Modifier.isInterface(signatureModifiers)) {
kind = Constants.INVOKEINTERFACE;
} else if (Modifier.isStatic(signatureModifiers)) {
kind = Constants.INVOKESTATIC;
} else if (Modifier.isPrivate(signatureModifiers) || signature.getName().equals("")) {
kind = Constants.INVOKESPECIAL;
} else {
kind = Constants.INVOKEVIRTUAL;
}
UnresolvedType targetType = signature.getDeclaringType();
if (targetType.isParameterizedType()) {
targetType = targetType.resolve(world).getGenericType();
}
return fact.createInvoke(targetType.getName(), signature.getName(), BcelWorld.makeBcelType(signature.getReturnType()),
BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind);
}
public static Instruction createGet(InstructionFactory fact, Member signature) {
short kind;
if (Modifier.isStatic(signature.getModifiers())) {
kind = Constants.GETSTATIC;
} else {
kind = Constants.GETFIELD;
}
return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(),
BcelWorld.makeBcelType(signature.getReturnType()), kind);
}
public static Instruction createSet(InstructionFactory fact, Member signature) {
short kind;
if (Modifier.isStatic(signature.getModifiers())) {
kind = Constants.PUTSTATIC;
} else {
kind = Constants.PUTFIELD;
}
return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(),
BcelWorld.makeBcelType(signature.getReturnType()), kind);
}
public static Instruction createInstanceof(InstructionFactory fact, ReferenceType t) {
int cpoolEntry = (t instanceof ArrayType) ? fact.getConstantPool().addArrayClass((ArrayType) t) : fact.getConstantPool()
.addClass((ObjectType) t);
return new InstructionCP(Constants.INSTANCEOF, cpoolEntry);
}
public static Instruction createInvoke(InstructionFactory fact, LazyMethodGen m) {
short kind;
if (m.getEnclosingClass().isInterface()) {
if (m.isStatic()) {
// For static methods on interfaces
kind = Constants.INVOKESTATIC;
} else {
kind = Constants.INVOKEINTERFACE;
}
} else if (m.isStatic()) {
kind = Constants.INVOKESTATIC;
} else if (m.isPrivate() || m.getName().equals("")) {
kind = Constants.INVOKESPECIAL;
} else {
kind = Constants.INVOKEVIRTUAL;
}
return fact.createInvoke(m.getClassName(), m.getName(), m.getReturnType(), m.getArgumentTypes(), kind, m.getEnclosingClass().isInterface());
}
/**
* Create an invoke instruction
*
* @param fact
* @param kind INVOKEINTERFACE, INVOKEVIRTUAL..
* @param member
* @return
*/
public static Instruction createInvoke(InstructionFactory fact, short kind, Member member) {
return fact.createInvoke(member.getDeclaringType().getName(), member.getName(),
BcelWorld.makeBcelType(member.getReturnType()), BcelWorld.makeBcelTypes(member.getParameterTypes()), kind);
}
private static String[] argNames = new String[] { "arg0", "arg1", "arg2", "arg3", "arg4" };
// ??? these should perhaps be cached. Remember to profile this to see if
// it's a problem.
public static String[] makeArgNames(int n) {
String[] ret = new String[n];
for (int i = 0; i < n; i++) {
if (i < 5) {
ret[i] = argNames[i];
} else {
ret[i] = "arg" + i;
}
}
return ret;
}
// Lookup table, for converting between pairs of types, it gives
// us the method name in the Conversions class
private static Hashtable validBoxing = new Hashtable<>();
static {
validBoxing.put("Ljava/lang/Byte;B", "byteObject");
validBoxing.put("Ljava/lang/Character;C", "charObject");
validBoxing.put("Ljava/lang/Double;D", "doubleObject");
validBoxing.put("Ljava/lang/Float;F", "floatObject");
validBoxing.put("Ljava/lang/Integer;I", "intObject");
validBoxing.put("Ljava/lang/Long;J", "longObject");
validBoxing.put("Ljava/lang/Short;S", "shortObject");
validBoxing.put("Ljava/lang/Boolean;Z", "booleanObject");
validBoxing.put("BLjava/lang/Byte;", "byteValue");
validBoxing.put("CLjava/lang/Character;", "charValue");
validBoxing.put("DLjava/lang/Double;", "doubleValue");
validBoxing.put("FLjava/lang/Float;", "floatValue");
validBoxing.put("ILjava/lang/Integer;", "intValue");
validBoxing.put("JLjava/lang/Long;", "longValue");
validBoxing.put("SLjava/lang/Short;", "shortValue");
validBoxing.put("ZLjava/lang/Boolean;", "booleanValue");
}
public static void appendConversion(InstructionList il, InstructionFactory fact, ResolvedType fromType, ResolvedType toType) {
if (!toType.isConvertableFrom(fromType) && !fromType.isConvertableFrom(toType)) {
throw new BCException("can't convert from " + fromType + " to " + toType);
}
// XXX I'm sure this test can be simpler but my brain hurts and this works
World w = toType.getWorld();
if (w == null) { // dbg349636
throw new IllegalStateException("Debug349636: Unexpectedly found world null for type " + toType.getName());
}
if (!w.isInJava5Mode()) {
if (toType.needsNoConversionFrom(fromType)) {
return;
}
} else {
if (toType.needsNoConversionFrom(fromType) && !(toType.isPrimitiveType() ^ fromType.isPrimitiveType())) {
return;
}
}
if (toType.equals(UnresolvedType.VOID)) {
// assert fromType.equals(UnresolvedType.OBJECT)
il.append(InstructionFactory.createPop(fromType.getSize()));
} else if (fromType.equals(UnresolvedType.VOID)) {
// assert toType.equals(UnresolvedType.OBJECT)
il.append(InstructionFactory.createNull(Type.OBJECT));
return;
} else if (fromType.equals(UnresolvedType.OBJECT)) {
Type to = BcelWorld.makeBcelType(toType);
if (toType.isPrimitiveType()) {
String name = toType.toString() + "Value";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT },
Constants.INVOKESTATIC));
} else {
il.append(fact.createCheckCast((ReferenceType) to));
}
} else if (toType.equals(UnresolvedType.OBJECT)) {
// assert fromType.isPrimitive()
Type from = BcelWorld.makeBcelType(fromType);
String name = fromType.toString() + "Object";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from },
Constants.INVOKESTATIC));
} else if (toType.getWorld().isInJava5Mode() && validBoxing.get(toType.getSignature() + fromType.getSignature()) != null) {
// XXX could optimize by using any java boxing code that may be just
// before the call...
Type from = BcelWorld.makeBcelType(fromType);
Type to = BcelWorld.makeBcelType(toType);
String name = validBoxing.get(toType.getSignature() + fromType.getSignature());
if (toType.isPrimitiveType()) {
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT },
Constants.INVOKESTATIC));
} else {
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from },
Constants.INVOKESTATIC));
il.append(fact.createCheckCast((ReferenceType) to));
}
} else if (fromType.isPrimitiveType()) {
// assert toType.isPrimitive()
Type from = BcelWorld.makeBcelType(fromType);
Type to = BcelWorld.makeBcelType(toType);
try {
Instruction i = fact.createCast(from, to);
if (i != null) {
il.append(i);
} else {
il.append(fact.createCast(from, Type.INT));
il.append(fact.createCast(Type.INT, to));
}
} catch (RuntimeException e) {
il.append(fact.createCast(from, Type.INT));
il.append(fact.createCast(Type.INT, to));
}
} else {
Type to = BcelWorld.makeBcelType(toType);
// assert ! fromType.isPrimitive() && ! toType.isPrimitive()
il.append(fact.createCheckCast((ReferenceType) to));
}
}
public static InstructionList createConversion(InstructionFactory factory, Type fromType, Type toType) {
return createConversion(factory, fromType, toType, false);
}
public static InstructionList createConversion(InstructionFactory fact, Type fromType, Type toType, boolean allowAutoboxing) {
// System.out.println("cast to: " + toType);
InstructionList il = new InstructionList();
// PR71273
if ((fromType.equals(Type.BYTE) || fromType.equals(Type.CHAR) || fromType.equals(Type.SHORT)) && (toType.equals(Type.INT))) {
return il;
}
if (fromType.equals(toType)) {
return il;
}
if (toType.equals(Type.VOID)) {
il.append(InstructionFactory.createPop(fromType.getSize()));
return il;
}
if (fromType.equals(Type.VOID)) {
if (toType instanceof BasicType) {
throw new BCException("attempting to cast from void to basic type");
}
il.append(InstructionFactory.createNull(Type.OBJECT));
return il;
}
if (fromType.equals(Type.OBJECT)) {
if (toType instanceof BasicType) {
String name = toType.toString() + "Value";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT },
Constants.INVOKESTATIC));
return il;
}
}
if (toType.equals(Type.OBJECT)) {
if (fromType instanceof BasicType) {
String name = fromType.toString() + "Object";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType },
Constants.INVOKESTATIC));
return il;
} else if (fromType instanceof ReferenceType) {
return il;
} else {
throw new RuntimeException();
}
}
if (fromType instanceof ReferenceType && ((ReferenceType) fromType).isAssignmentCompatibleWith(toType)) {
return il;
}
if (allowAutoboxing) {
if (toType instanceof BasicType && fromType instanceof ReferenceType) {
// unboxing
String name = toType.toString() + "Value";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT },
Constants.INVOKESTATIC));
return il;
}
if (fromType instanceof BasicType && toType instanceof ReferenceType) {
// boxing
String name = fromType.toString() + "Object";
il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType },
Constants.INVOKESTATIC));
il.append(fact.createCast(Type.OBJECT, toType));
return il;
}
}
il.append(fact.createCast(fromType, toType));
return il;
}
public static Instruction createConstant(InstructionFactory fact, int value) {
Instruction inst;
switch (value) {
case -1:
inst = InstructionConstants.ICONST_M1;
break;
case 0:
inst = InstructionConstants.ICONST_0;
break;
case 1:
inst = InstructionConstants.ICONST_1;
break;
case 2:
inst = InstructionConstants.ICONST_2;
break;
case 3:
inst = InstructionConstants.ICONST_3;
break;
case 4:
inst = InstructionConstants.ICONST_4;
break;
case 5:
inst = InstructionConstants.ICONST_5;
break;
default:
if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
inst = new InstructionByte(Constants.BIPUSH, (byte) value);
} else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
inst = new InstructionShort(Constants.SIPUSH, (short) value);
} else {
int ii = fact.getClassGen().getConstantPool().addInteger(value);
inst = new InstructionCP(value <= Constants.MAX_BYTE ? Constants.LDC : Constants.LDC_W, ii);
}
break;
}
return inst;
}
/** For testing purposes: bit clunky but does work */
public static int testingParseCounter = 0;
public static JavaClass makeJavaClass(String filename, byte[] bytes) {
try {
testingParseCounter++;
ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), filename);
return parser.parse();
} catch (IOException e) {
throw new BCException("malformed class file");
}
}
/**
* replace an instruction handle with another instruction, in this case, a branch instruction.
*
* @param ih the instruction handle to replace.
* @param replacementInstructions the branch instruction to replace ih with
* @param enclosingMethod where to find ih's instruction list.
*/
public static void replaceInstruction(InstructionHandle ih, InstructionList replacementInstructions,
LazyMethodGen enclosingMethod) {
InstructionList il = enclosingMethod.getBody();
InstructionHandle fresh = il.append(ih, replacementInstructions);
deleteInstruction(ih, fresh, enclosingMethod);
}
/**
* delete an instruction handle and retarget all targeters of the deleted instruction to the next instruction. Obviously, this
* should not be used to delete a control transfer instruction unless you know what you're doing.
*
* @param ih the instruction handle to delete.
* @param enclosingMethod where to find ih's instruction list.
*/
public static void deleteInstruction(InstructionHandle ih, LazyMethodGen enclosingMethod) {
deleteInstruction(ih, ih.getNext(), enclosingMethod);
}
/**
* delete an instruction handle and retarget all targeters of the deleted instruction to the provided target.
*
* @param ih the instruction handle to delete
* @param retargetTo the instruction handle to retarget targeters of ih to.
* @param enclosingMethod where to find ih's instruction list.
*/
public static void deleteInstruction(InstructionHandle ih, InstructionHandle retargetTo, LazyMethodGen enclosingMethod) {
InstructionList il = enclosingMethod.getBody();
for (InstructionTargeter targeter : ih.getTargetersCopy()) {
targeter.updateTarget(ih, retargetTo);
}
ih.removeAllTargeters();
try {
il.delete(ih);
} catch (TargetLostException e) {
throw new BCException("this really can't happen");
}
}
/**
* Fix for Bugzilla #39479, #40109 patch contributed by Andy Clement
*
* Need to manually copy Select instructions - if we rely on the the 'fresh' object created by copy(), the InstructionHandle
* array 'targets' inside the Select object will not have been deep copied, so modifying targets in fresh will modify the
* original Select - not what we want ! (It is a bug in BCEL to do with cloning Select objects).
*
*
* declare error:
* call(* Instruction.copy()) && within(org.aspectj.weaver)
* && !withincode(* Utility.copyInstruction(Instruction)):
* "use Utility.copyInstruction to work-around bug in Select.copy()";
*
*/
public static Instruction copyInstruction(Instruction i) {
if (i instanceof InstructionSelect) {
InstructionSelect freshSelect = (InstructionSelect) i;
// Create a new targets array that looks just like the existing one
InstructionHandle[] targets = new InstructionHandle[freshSelect.getTargets().length];
for (int ii = 0; ii < targets.length; ii++) {
targets[ii] = freshSelect.getTargets()[ii];
}
// Create a new select statement with the new targets array
return new SwitchBuilder(freshSelect.getMatchs(), targets, freshSelect.getTarget()).getInstruction();
} else {
return i.copy(); // Use clone for shallow copy...
}
}
/** returns -1 if no source line attribute */
// this naive version overruns the JVM stack size, if only Java understood
// tail recursion...
// public static int getSourceLine(InstructionHandle ih) {
// if (ih == null) return -1;
//
// InstructionTargeter[] ts = ih.getTargeters();
// if (ts != null) {
// for (int j = ts.length - 1; j >= 0; j--) {
// InstructionTargeter t = ts[j];
// if (t instanceof LineNumberTag) {
// return ((LineNumberTag)t).getLineNumber();
// }
// }
// }
// return getSourceLine(ih.getNext());
// }
public static int getSourceLine(InstructionHandle ih) {// ,boolean
// goforwards) {
int lookahead = 0;
// arbitrary rule that we will never lookahead more than 100
// instructions for a line #
while (lookahead++ < 100) {
if (ih == null) {
return -1;
}
for (InstructionTargeter t : ih.getTargeters()) {
if (t instanceof LineNumberTag) {
return ((LineNumberTag) t).getLineNumber();
}
}
// if (goforwards) ih=ih.getNext(); else
ih = ih.getPrev();
}
// System.err.println("no line information available for: " + ih);
return -1;
}
// public static int getSourceLine(InstructionHandle ih) {
// return getSourceLine(ih,false);
// }
// assumes that there is no already extant source line tag. Otherwise we'll
// have to be better.
public static void setSourceLine(InstructionHandle ih, int lineNumber) {
// OPTIMIZE LineNumberTag instances for the same line could be shared
// throughout a method...
ih.addTargeter(new LineNumberTag(lineNumber));
}
public static int makePublic(int i) {
return i & ~(Modifier.PROTECTED | Modifier.PRIVATE) | Modifier.PUBLIC;
}
public static BcelVar[] pushAndReturnArrayOfVars(ResolvedType[] proceedParamTypes, InstructionList il, InstructionFactory fact,
LazyMethodGen enclosingMethod) {
int len = proceedParamTypes.length;
BcelVar[] ret = new BcelVar[len];
for (int i = len - 1; i >= 0; i--) {
ResolvedType typeX = proceedParamTypes[i];
Type type = BcelWorld.makeBcelType(typeX);
int local = enclosingMethod.allocateLocal(type);
il.append(InstructionFactory.createStore(type, local));
ret[i] = new BcelVar(typeX, local);
}
return ret;
}
public static boolean isConstantPushInstruction(Instruction i) {
long ii = Constants.instFlags[i.opcode];
return ((ii & Constants.PUSH_INST) != 0 && (ii & Constants.CONSTANT_INST) != 0);
}
/**
* Checks for suppression specified on the member or on the declaring type of that member
*/
public static boolean isSuppressing(Member member, String lintkey) {
boolean isSuppressing = Utils.isSuppressing(member.getAnnotations(), lintkey);
if (isSuppressing) {
return true;
}
UnresolvedType type = member.getDeclaringType();
if (type instanceof ResolvedType) {
return Utils.isSuppressing(((ResolvedType) type).getAnnotations(), lintkey);
}
return false;
}
public static List getSuppressedWarnings(AnnotationAJ[] anns, Lint lint) {
if (anns == null) {
return Collections.emptyList();
}
// Go through the annotation types
List suppressedWarnings = new ArrayList<>();
boolean found = false;
for (int i = 0; !found && i < anns.length; i++) {
// Check for the SuppressAjWarnings annotation
if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals(
((BcelAnnotation) anns[i]).getBcelAnnotation().getTypeSignature())) {
found = true;
// Two possibilities:
// 1. there are no values specified (i.e. @SuppressAjWarnings)
// 2. there are values specified (i.e. @SuppressAjWarnings("A")
// or @SuppressAjWarnings({"A","B"})
List vals = ((BcelAnnotation) anns[i]).getBcelAnnotation().getValues();
if (vals == null || vals.isEmpty()) { // (1)
suppressedWarnings.addAll(lint.allKinds());
} else { // (2)
// We know the value is an array value
ArrayElementValue array = (ArrayElementValue) (vals.get(0)).getValue();
ElementValue[] values = array.getElementValuesArray();
for (ElementValue elementValue : values) {
// We know values in the array are strings
SimpleElementValue value = (SimpleElementValue) elementValue;
Lint.Kind lintKind = lint.getLintKind(value.getValueString());
if (lintKind != null) {
suppressedWarnings.add(lintKind);
}
}
}
}
}
return suppressedWarnings;
}
// not yet used...
// public static boolean isSimple(Method method) {
// if (method.getCode()==null) return true;
// if (method.getCode().getCode().length>10) return false;
// InstructionList instrucs = new
// InstructionList(method.getCode().getCode()); // expensive!
// InstructionHandle InstrHandle = instrucs.getStart();
// while (InstrHandle != null) {
// Instruction Instr = InstrHandle.getInstruction();
// int opCode = Instr.opcode;
// // if current instruction is a branch instruction, see if it's a backward
// branch.
// // if it is return immediately (can't be trivial)
// if (Instr instanceof InstructionBranch) {
// // InstructionBranch BI = (InstructionBranch) Instr;
// if (Instr.getIndex() < 0) return false;
// } else if (Instr instanceof InvokeInstruction) {
// // if current instruction is an invocation, indicate that it can't be
// trivial
// return false;
// }
// InstrHandle = InstrHandle.getNext();
// }
// return true;
// }
public static Attribute bcelAttribute(AjAttribute a, ConstantPool pool) {
int nameIndex = pool.addUtf8(a.getNameString());
byte[] bytes = a.getBytes(new BcelConstantPoolWriter(pool));
int length = bytes.length;
return new Unknown(nameIndex, length, bytes, pool);
}
}