|
|
|
@ -22,160 +22,162 @@ import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; |
|
|
|
|
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; |
|
|
|
|
import org.jetbrains.java.decompiler.struct.gen.VarType; |
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayInputStream; |
|
|
|
|
import java.io.DataInputStream; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.Collections; |
|
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
|
|
public class StructAnnotationAttribute extends StructGeneralAttribute { |
|
|
|
|
|
|
|
|
|
private List<AnnotationExprent> annotations; |
|
|
|
|
|
|
|
|
|
public void initContent(ConstantPool pool) { |
|
|
|
|
|
|
|
|
|
super.initContent(pool); |
|
|
|
|
|
|
|
|
|
annotations = new ArrayList<AnnotationExprent>(); |
|
|
|
|
DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 2, info.length)); |
|
|
|
|
@Override |
|
|
|
|
public void initContent(ConstantPool pool) throws IOException { |
|
|
|
|
annotations = parseAnnotations(pool, stream()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int len = (((info[0] & 0xFF) << 8) | (info[1] & 0xFF)); |
|
|
|
|
for (int i = 0; i < len; i++) { |
|
|
|
|
annotations.add(parseAnnotation(data, pool)); |
|
|
|
|
public static List<AnnotationExprent> parseAnnotations(ConstantPool pool, DataInputStream data) throws IOException { |
|
|
|
|
int len = data.readUnsignedShort(); |
|
|
|
|
if (len > 0) { |
|
|
|
|
List<AnnotationExprent> annotations = new ArrayList<AnnotationExprent>(len); |
|
|
|
|
for (int i = 0; i < len; i++) { |
|
|
|
|
annotations.add(parseAnnotation(data, pool)); |
|
|
|
|
} |
|
|
|
|
return annotations; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
return Collections.emptyList(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) { |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
|
|
String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
VarType cltype = new VarType(classname); |
|
|
|
|
|
|
|
|
|
int len = data.readUnsignedShort(); |
|
|
|
|
|
|
|
|
|
List<String> parnames = new ArrayList<String>(); |
|
|
|
|
List<Exprent> parvalues = new ArrayList<Exprent>(); |
|
|
|
|
public static AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) throws IOException { |
|
|
|
|
String className = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
|
|
|
|
|
List<String> names; |
|
|
|
|
List<Exprent> values; |
|
|
|
|
int len = data.readUnsignedShort(); |
|
|
|
|
if (len > 0) { |
|
|
|
|
names = new ArrayList<String>(len); |
|
|
|
|
values = new ArrayList<Exprent>(len); |
|
|
|
|
for (int i = 0; i < len; i++) { |
|
|
|
|
parnames.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); |
|
|
|
|
parvalues.add(parseAnnotationElement(data, pool)); |
|
|
|
|
names.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); |
|
|
|
|
values.add(parseAnnotationElement(data, pool)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return new AnnotationExprent(cltype.value, parnames, parvalues); |
|
|
|
|
} |
|
|
|
|
catch (IOException ex) { |
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
else { |
|
|
|
|
names = Collections.emptyList(); |
|
|
|
|
values = Collections.emptyList(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) { |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
int tag = data.readUnsignedByte(); |
|
|
|
|
|
|
|
|
|
switch (tag) { |
|
|
|
|
case 'e': // enum constant
|
|
|
|
|
String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
String constname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
|
|
|
|
|
FieldDescriptor descr = FieldDescriptor.parseDescriptor(classname); |
|
|
|
|
return new FieldExprent(constname, descr.type.value, true, null, descr); |
|
|
|
|
case 'c': // class
|
|
|
|
|
String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
VarType type = FieldDescriptor.parseDescriptor(descriptor).type; |
|
|
|
|
|
|
|
|
|
String value; |
|
|
|
|
switch (type.type) { |
|
|
|
|
case CodeConstants.TYPE_OBJECT: |
|
|
|
|
value = type.value; |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_BYTE: |
|
|
|
|
value = byte.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_CHAR: |
|
|
|
|
value = char.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_DOUBLE: |
|
|
|
|
value = double.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_FLOAT: |
|
|
|
|
value = float.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_INT: |
|
|
|
|
value = int.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_LONG: |
|
|
|
|
value = long.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_SHORT: |
|
|
|
|
value = short.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_BOOLEAN: |
|
|
|
|
value = boolean.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_VOID: |
|
|
|
|
value = void.class.getName(); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid class type: " + type.type); |
|
|
|
|
} |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_CLASS, value); |
|
|
|
|
case '[': // array
|
|
|
|
|
int len = data.readUnsignedShort(); |
|
|
|
|
List<Exprent> lst = new ArrayList<Exprent>(); |
|
|
|
|
return new AnnotationExprent(new VarType(className).value, names, values); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) throws IOException { |
|
|
|
|
int tag = data.readUnsignedByte(); |
|
|
|
|
|
|
|
|
|
switch (tag) { |
|
|
|
|
case 'e': // enum constant
|
|
|
|
|
String className = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
String constName = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
FieldDescriptor descr = FieldDescriptor.parseDescriptor(className); |
|
|
|
|
return new FieldExprent(constName, descr.type.value, true, null, descr); |
|
|
|
|
|
|
|
|
|
case 'c': // class
|
|
|
|
|
String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); |
|
|
|
|
VarType type = FieldDescriptor.parseDescriptor(descriptor).type; |
|
|
|
|
|
|
|
|
|
String value; |
|
|
|
|
switch (type.type) { |
|
|
|
|
case CodeConstants.TYPE_OBJECT: |
|
|
|
|
value = type.value; |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_BYTE: |
|
|
|
|
value = byte.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_CHAR: |
|
|
|
|
value = char.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_DOUBLE: |
|
|
|
|
value = double.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_FLOAT: |
|
|
|
|
value = float.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_INT: |
|
|
|
|
value = int.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_LONG: |
|
|
|
|
value = long.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_SHORT: |
|
|
|
|
value = short.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_BOOLEAN: |
|
|
|
|
value = boolean.class.getName(); |
|
|
|
|
break; |
|
|
|
|
case CodeConstants.TYPE_VOID: |
|
|
|
|
value = void.class.getName(); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid class type: " + type.type); |
|
|
|
|
} |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_CLASS, value); |
|
|
|
|
|
|
|
|
|
case '[': // array
|
|
|
|
|
List<Exprent> elements = Collections.emptyList(); |
|
|
|
|
int len = data.readUnsignedShort(); |
|
|
|
|
if (len > 0) { |
|
|
|
|
elements = new ArrayList<Exprent>(len); |
|
|
|
|
for (int i = 0; i < len; i++) { |
|
|
|
|
lst.add(parseAnnotationElement(data, pool)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VarType newtype; |
|
|
|
|
if (lst.isEmpty()) { |
|
|
|
|
newtype = new VarType(CodeConstants.TYPE_OBJECT, 1, "java/lang/Object"); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
VarType eltype = lst.get(0).getExprType(); |
|
|
|
|
newtype = new VarType(eltype.type, 1, eltype.value); |
|
|
|
|
elements.add(parseAnnotationElement(data, pool)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
NewExprent newexpr = new NewExprent(newtype, new ArrayList<Exprent>()); |
|
|
|
|
newexpr.setDirectArrayInit(true); |
|
|
|
|
newexpr.setLstArrayElements(lst); |
|
|
|
|
return newexpr; |
|
|
|
|
case '@': // annotation
|
|
|
|
|
return parseAnnotation(data, pool); |
|
|
|
|
default: |
|
|
|
|
PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); |
|
|
|
|
switch (tag) { |
|
|
|
|
case 'B': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); |
|
|
|
|
case 'C': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); |
|
|
|
|
case 'D': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); |
|
|
|
|
case 'F': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); |
|
|
|
|
case 'I': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_INT, cn.value); |
|
|
|
|
case 'J': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_LONG, cn.value); |
|
|
|
|
case 'S': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); |
|
|
|
|
case 'Z': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); |
|
|
|
|
case 's': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_STRING, cn.value); |
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid element type!"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (IOException ex) { |
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VarType newType; |
|
|
|
|
if (elements.isEmpty()) { |
|
|
|
|
newType = new VarType(CodeConstants.TYPE_OBJECT, 1, "java/lang/Object"); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
VarType elementType = elements.get(0).getExprType(); |
|
|
|
|
newType = new VarType(elementType.type, 1, elementType.value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
NewExprent newExpr = new NewExprent(newType, Collections.<Exprent>emptyList()); |
|
|
|
|
newExpr.setDirectArrayInit(true); |
|
|
|
|
newExpr.setLstArrayElements(elements); |
|
|
|
|
return newExpr; |
|
|
|
|
|
|
|
|
|
case '@': // annotation
|
|
|
|
|
return parseAnnotation(data, pool); |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); |
|
|
|
|
switch (tag) { |
|
|
|
|
case 'B': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); |
|
|
|
|
case 'C': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); |
|
|
|
|
case 'D': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); |
|
|
|
|
case 'F': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); |
|
|
|
|
case 'I': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_INT, cn.value); |
|
|
|
|
case 'J': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_LONG, cn.value); |
|
|
|
|
case 'S': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); |
|
|
|
|
case 'Z': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); |
|
|
|
|
case 's': |
|
|
|
|
return new ConstExprent(VarType.VARTYPE_STRING, cn.value); |
|
|
|
|
default: |
|
|
|
|
throw new RuntimeException("invalid element type!"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<AnnotationExprent> getAnnotations() { |
|
|
|
|
return annotations; |
|
|
|
|
} |
|
|
|
|