org.apache.ibatis.javassist.bytecode.SignatureAttribute Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mybatis Show documentation
Show all versions of mybatis Show documentation
The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented
applications. MyBatis couples objects with stored procedures or SQL statements using a XML descriptor or
annotations. Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping
tools.
The newest version!
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package org.apache.ibatis.javassist.bytecode;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.javassist.CtClass;
/**
* Signature_attribute
.
*/
public class SignatureAttribute extends AttributeInfo {
/**
* The name of this attribute "Signature"
.
*/
public static final String tag = "Signature";
SignatureAttribute(ConstPool cp, int n, DataInputStream in)
throws IOException
{
super(cp, n, in);
}
/**
* Constructs a Signature
attribute.
*
* @param cp a constant pool table.
* @param signature the signature represented by this attribute.
*/
public SignatureAttribute(ConstPool cp, String signature) {
super(cp, tag);
int index = cp.addUtf8Info(signature);
byte[] bvalue = new byte[2];
bvalue[0] = (byte)(index >>> 8);
bvalue[1] = (byte)index;
set(bvalue);
}
/**
* Returns the generic signature indicated by signature_index
.
*
* @see #toClassSignature(String)
* @see #toMethodSignature(String)
* @see #toFieldSignature(String)
*/
public String getSignature() {
return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0));
}
/**
* Sets signature_index
to the index of the given generic signature,
* which is added to a constant pool.
*
* @param sig new signature.
* @since 3.11
*/
public void setSignature(String sig) {
int index = getConstPool().addUtf8Info(sig);
ByteArray.write16bit(index, info, 0);
}
/**
* Makes a copy. Class names are replaced according to the
* given Map
object.
*
* @param newCp the constant pool table used by the new copy.
* @param classnames pairs of replaced and substituted
* class names.
*/
@Override
public AttributeInfo copy(ConstPool newCp, Map classnames) {
return new SignatureAttribute(newCp, getSignature());
}
@Override
void renameClass(String oldname, String newname) {
String sig = renameClass(getSignature(), oldname, newname);
setSignature(sig);
}
@Override
void renameClass(Map classnames) {
String sig = renameClass(getSignature(), classnames);
setSignature(sig);
}
static String renameClass(String desc, String oldname, String newname) {
Map map = new HashMap();
map.put(oldname, newname);
return renameClass(desc, map);
}
static String renameClass(String desc, Map map) {
if (map == null || map.isEmpty())
return desc;
StringBuilder newdesc = new StringBuilder();
int head = 0;
int i = 0;
for (;;) {
final int j = desc.indexOf('L', i);
if (j < 0)
break;
final ArrayList nameBufs = new ArrayList<>();
final ArrayList genericParamBufs = new ArrayList<>();
i = parseClassName(nameBufs, genericParamBufs, desc, j) + 1;
if (i < 0)
break;
String name = String.join("$", nameBufs.toArray(new StringBuilder[0]));
String newname = map.get(name);
if (newname != null) {
if (makeNewClassName(desc, map, name, newname, newdesc, head, j,
nameBufs, genericParamBufs))
head = i;
}
else
if (replaceTypeArguments(desc, map, newdesc, head, j,
nameBufs, genericParamBufs))
head = i;
}
if (head == 0)
return desc;
int len = desc.length();
if (head < len)
newdesc.append(desc.substring(head, len));
return newdesc.toString();
}
private static int parseClassName(ArrayList nameBufs,
ArrayList genericParamBufs,
String desc, int j)
{
StringBuilder nameBuf = new StringBuilder();
StringBuilder genericParamBuf = new StringBuilder();
int k = j;
char c;
try {
while ((c = desc.charAt(++k)) != ';') {
if (c == '<') {
genericParamBuf.append(c);
int level = 1;
while (level > 0) {
c = desc.charAt(++k);
genericParamBuf.append(c);
if (c == '<')
++level;
else if (c == '>')
--level;
}
}
else if (c == '.') {
nameBufs.add(nameBuf);
genericParamBufs.add(genericParamBuf);
nameBuf = new StringBuilder();
genericParamBuf = new StringBuilder();
}
else
nameBuf.append(c);
}
}
catch (IndexOutOfBoundsException e) {
return -2; // error
}
nameBufs.add(nameBuf);
genericParamBufs.add(genericParamBuf);
return k;
}
private static boolean makeNewClassName(String desc,
Map map, String name,
String newname, StringBuilder newdesc,
int head, int j,
ArrayList nameBufs,
ArrayList genericParamBufs)
{
final String[] nameSplit = name.split("\\$");
final String[] newnameSplit = newname.split("\\$");
if (nameSplit.length == newnameSplit.length) {
final String[] newnames = new String[nameBufs.size()];
for (int start = 0, z = 0; z < nameBufs.size(); z++) {
final int toAggregate = (int) nameBufs.get(z).chars().filter(ch -> ch == '$').count() + 1;
String s = String.join("$", Arrays.copyOfRange(newnameSplit, start, start + toAggregate));
start += toAggregate;
newnames[z] = s;
}
newdesc.append(desc.substring(head, j));
newdesc.append('L');
for (int z = 0; z < newnames.length; z++) {
if (z > 0)
newdesc.append('.');
newdesc.append(newnames[z]);
final String newgenericParam;
final StringBuilder genericParamBufCurrent = genericParamBufs.get(z);
if (genericParamBufCurrent.length() > 0)
newgenericParam = "<" + renameClass(genericParamBufCurrent.substring(1, genericParamBufCurrent.length() - 1), map) + ">";
else
newgenericParam = genericParamBufCurrent.toString(); //empty string
newdesc.append(newgenericParam);
}
newdesc.append(';'); //the final semicolon
return true;
}
else
return false;
}
private static boolean replaceTypeArguments(String desc,
Map map,
StringBuilder newdesc,
int head, int j,
ArrayList nameBufs,
ArrayList genericParamBufs)
{
final ArrayList newGenericParamBufs = new ArrayList();
boolean changed = false;
for (int z = 0; z < genericParamBufs.size(); z++) {
final String newGenericParam;
final StringBuilder genericParamBufCurrent = genericParamBufs.get(z);
if (genericParamBufCurrent.length() > 0) {
newGenericParam = "<" + renameClass(genericParamBufCurrent.substring(1, genericParamBufCurrent.length() - 1), map) + ">";
changed = changed || !genericParamBufCurrent.toString().equals(newGenericParam);
}
else
newGenericParam = genericParamBufCurrent.toString(); //empty string
newGenericParamBufs.add(newGenericParam);
}
if (changed) {
newdesc.append(desc.substring(head, j));
newdesc.append('L');
for (int z = 0; z < genericParamBufs.size(); z++) {
if (z > 0)
newdesc.append('.');
newdesc.append(nameBufs.get(z));
newdesc.append(newGenericParamBufs.get(z));
}
newdesc.append(';');
return true;
}
else
return false;
}
@SuppressWarnings("unused")
private static boolean isNamePart(int c) {
return c != ';' && c != '<';
}
static private class Cursor {
int position = 0;
int indexOf(String s, int ch) throws BadBytecode {
int i = s.indexOf(ch, position);
if (i < 0)
throw error(s);
position = i + 1;
return i;
}
}
/**
* Class signature.
*/
public static class ClassSignature {
TypeParameter[] params;
ClassType superClass;
ClassType[] interfaces;
/**
* Constructs a class signature.
*
* @param params type parameters.
* @param superClass the super class.
* @param interfaces the interface types.
*/
public ClassSignature(TypeParameter[] params, ClassType superClass, ClassType[] interfaces) {
this.params = params == null ? new TypeParameter[0] : params;
this.superClass = superClass == null ? ClassType.OBJECT : superClass;
this.interfaces = interfaces == null ? new ClassType[0] : interfaces;
}
/**
* Constructs a class signature.
*
* @param p type parameters.
*/
public ClassSignature(TypeParameter[] p) {
this(p, null, null);
}
/**
* Returns the type parameters.
*
* @return a zero-length array if the type parameters are not specified.
*/
public TypeParameter[] getParameters() {
return params;
}
/**
* Returns the super class.
*/
public ClassType getSuperClass() { return superClass; }
/**
* Returns the super interfaces.
*
* @return a zero-length array if the super interfaces are not specified.
*/
public ClassType[] getInterfaces() { return interfaces; }
/**
* Returns the string representation.
*/
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
TypeParameter.toString(sbuf, params);
sbuf.append(" extends ").append(superClass);
if (interfaces.length > 0) {
sbuf.append(" implements ");
Type.toString(sbuf, interfaces);
}
return sbuf.toString();
}
/**
* Returns the encoded string representing the method type signature.
*/
public String encode() {
StringBuilder sbuf = new StringBuilder();
if (params.length > 0) {
sbuf.append('<');
for (int i = 0; i < params.length; i++)
params[i].encode(sbuf);
sbuf.append('>');
}
superClass.encode(sbuf);
for (int i = 0; i < interfaces.length; i++)
interfaces[i].encode(sbuf);
return sbuf.toString();
}
}
/**
* Method type signature.
*/
public static class MethodSignature {
TypeParameter[] typeParams;
Type[] params;
Type retType;
ObjectType[] exceptions;
/**
* Constructs a method type signature. Any parameter can be null
* to represent void
or nothing.
*
* @param tp type parameters.
* @param params parameter types.
* @param ret a return type, or null if the return type is void
.
* @param ex exception types.
*/
public MethodSignature(TypeParameter[] tp, Type[] params, Type ret, ObjectType[] ex) {
typeParams = tp == null ? new TypeParameter[0] : tp;
this.params = params == null ? new Type[0] : params;
retType = ret == null ? new BaseType("void") : ret;
exceptions = ex == null ? new ObjectType[0] : ex;
}
/**
* Returns the formal type parameters.
*
* @return a zero-length array if the type parameters are not specified.
*/
public TypeParameter[] getTypeParameters() { return typeParams; }
/**
* Returns the types of the formal parameters.
*
* @return a zero-length array if no formal parameter is taken.
*/
public Type[] getParameterTypes() { return params; }
/**
* Returns the type of the returned value.
*/
public Type getReturnType() { return retType; }
/**
* Returns the types of the exceptions that may be thrown.
*
* @return a zero-length array if exceptions are never thrown or
* the exception types are not parameterized types or type variables.
*/
public ObjectType[] getExceptionTypes() { return exceptions; }
/**
* Returns the string representation.
*/
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
TypeParameter.toString(sbuf, typeParams);
sbuf.append(" (");
Type.toString(sbuf, params);
sbuf.append(") ");
sbuf.append(retType);
if (exceptions.length > 0) {
sbuf.append(" throws ");
Type.toString(sbuf, exceptions);
}
return sbuf.toString();
}
/**
* Returns the encoded string representing the method type signature.
*/
public String encode() {
StringBuilder sbuf = new StringBuilder();
if (typeParams.length > 0) {
sbuf.append('<');
for (int i = 0; i < typeParams.length; i++)
typeParams[i].encode(sbuf);
sbuf.append('>');
}
sbuf.append('(');
for (int i = 0; i < params.length; i++)
params[i].encode(sbuf);
sbuf.append(')');
retType.encode(sbuf);
if (exceptions.length > 0)
for (int i = 0; i < exceptions.length; i++) {
sbuf.append('^');
exceptions[i].encode(sbuf);
}
return sbuf.toString();
}
}
/**
* Formal type parameters.
*
* @see TypeArgument
*/
public static class TypeParameter {
String name;
ObjectType superClass;
ObjectType[] superInterfaces;
TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) {
name = sig.substring(nb, ne);
superClass = sc;
superInterfaces = si;
}
/**
* Constructs a TypeParameter
representing a type parametre
* like <T extends ... >
.
*
* @param name parameter name.
* @param superClass an upper bound class-type (or null).
* @param superInterfaces an upper bound interface-type (or null).
*/
public TypeParameter(String name, ObjectType superClass, ObjectType[] superInterfaces) {
this.name = name;
this.superClass = superClass;
if (superInterfaces == null)
this.superInterfaces = new ObjectType[0];
else
this.superInterfaces = superInterfaces;
}
/**
* Constructs a TypeParameter
representing a type parameter
* like <T>
.
*
* @param name parameter name.
*/
public TypeParameter(String name) {
this(name, null, null);
}
/**
* Returns the name of the type parameter.
*/
public String getName() {
return name;
}
/**
* Returns the class bound of this parameter.
*/
public ObjectType getClassBound() { return superClass; }
/**
* Returns the interface bound of this parameter.
*
* @return a zero-length array if the interface bound is not specified.
*/
public ObjectType[] getInterfaceBound() { return superInterfaces; }
/**
* Returns the string representation.
*/
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder(getName());
if (superClass != null)
sbuf.append(" extends ").append(superClass.toString());
int len = superInterfaces.length;
if (len > 0) {
for (int i = 0; i < len; i++) {
if (i > 0 || superClass != null)
sbuf.append(" & ");
else
sbuf.append(" extends ");
sbuf.append(superInterfaces[i].toString());
}
}
return sbuf.toString();
}
static void toString(StringBuilder sbuf, TypeParameter[] tp) {
sbuf.append('<');
for (int i = 0; i < tp.length; i++) {
if (i > 0)
sbuf.append(", ");
sbuf.append(tp[i]);
}
sbuf.append('>');
}
void encode(StringBuilder sb) {
sb.append(name);
if (superClass == null)
sb.append(":Ljava/lang/Object;");
else {
sb.append(':');
superClass.encode(sb);
}
for (int i = 0; i < superInterfaces.length; i++) {
sb.append(':');
superInterfaces[i].encode(sb);
}
}
}
/**
* Type argument.
*
* @see TypeParameter
*/
public static class TypeArgument {
ObjectType arg;
char wildcard;
TypeArgument(ObjectType a, char w) {
arg = a;
wildcard = w;
}
/**
* Constructs a TypeArgument
.
* A type argument is <String>
, <int[]>
,
* or a type variable <T>
, etc.
*
* @param t a class type, an array type, or a type variable.
*/
public TypeArgument(ObjectType t) {
this(t, ' ');
}
/**
* Constructs a TypeArgument
representing <?>
.
*/
public TypeArgument() {
this(null, '*');
}
/**
* A factory method constructing a TypeArgument
with an upper bound.
* It represents <? extends ... >
*
* @param t an upper bound type.
*/
public static TypeArgument subclassOf(ObjectType t) {
return new TypeArgument(t, '+');
}
/**
* A factory method constructing a TypeArgument
with an lower bound.
* It represents <? super ... >
*
* @param t an lower bbound type.
*/
public static TypeArgument superOf(ObjectType t) {
return new TypeArgument(t, '-');
}
/**
* Returns the kind of this type argument.
*
* @return ' '
(not-wildcard), '*'
(wildcard), '+'
(wildcard with
* upper bound), or '-'
(wildcard with lower bound).
*/
public char getKind() { return wildcard; }
/**
* Returns true if this type argument is a wildcard type
* such as ?
, ? extends String
, or ? super Integer
.
*/
public boolean isWildcard() { return wildcard != ' '; }
/**
* Returns the type represented by this argument
* if the argument is not a wildcard type. Otherwise, this method
* returns the upper bound (if the kind is '+'),
* the lower bound (if the kind is '-'), or null (if the upper or lower
* bound is not specified).
*/
public ObjectType getType() { return arg; }
/**
* Returns the string representation.
*/
@Override
public String toString() {
if (wildcard == '*')
return "?";
String type = arg.toString();
if (wildcard == ' ')
return type;
else if (wildcard == '+')
return "? extends " + type;
else
return "? super " + type;
}
static void encode(StringBuilder sb, TypeArgument[] args) {
sb.append('<');
for (int i = 0; i < args.length; i++) {
TypeArgument ta = args[i];
if (ta.isWildcard())
sb.append(ta.wildcard);
if (ta.getType() != null)
ta.getType().encode(sb);
}
sb.append('>');
}
}
/**
* Primitive types and object types.
*/
public static abstract class Type {
abstract void encode(StringBuilder sb);
static void toString(StringBuilder sbuf, Type[] ts) {
for (int i = 0; i < ts.length; i++) {
if (i > 0)
sbuf.append(", ");
sbuf.append(ts[i]);
}
}
/**
* Returns the type name in the JVM internal style.
* For example, if the type is a nested class {@code foo.Bar.Baz},
* then {@code foo.Bar$Baz} is returned.
*/
public String jvmTypeName() { return toString(); }
}
/**
* Primitive types.
*/
public static class BaseType extends Type {
char descriptor;
BaseType(char c) { descriptor = c; }
/**
* Constructs a BaseType
.
*
* @param typeName void
, int
, ...
*/
public BaseType(String typeName) {
this(Descriptor.of(typeName).charAt(0));
}
/**
* Returns the descriptor representing this primitive type.
*
* @see javassist.bytecode.Descriptor
*/
public char getDescriptor() { return descriptor; }
/**
* Returns the CtClass
representing this
* primitive type.
*/
public CtClass getCtlass() {
return Descriptor.toPrimitiveClass(descriptor);
}
/**
* Returns the string representation.
*/
@Override
public String toString() {
return Descriptor.toClassName(Character.toString(descriptor));
}
@Override
void encode(StringBuilder sb) {
sb.append(descriptor);
}
}
/**
* Class types, array types, and type variables.
* This class is also used for representing a field type.
*/
public static abstract class ObjectType extends Type {
/**
* Returns the encoded string representing the object type signature.
*/
public String encode() {
StringBuilder sb = new StringBuilder();
encode(sb);
return sb.toString();
}
}
/**
* Class types.
*/
public static class ClassType extends ObjectType {
String name;
TypeArgument[] arguments;
static ClassType make(String s, int b, int e,
TypeArgument[] targs, ClassType parent) {
if (parent == null)
return new ClassType(s, b, e, targs);
return new NestedClassType(s, b, e, targs, parent);
}
ClassType(String signature, int begin, int end, TypeArgument[] targs) {
name = signature.substring(begin, end).replace('/', '.');
arguments = targs;
}
/**
* A class type representing java.lang.Object
.
*/
public static ClassType OBJECT = new ClassType("java.lang.Object", null);
/**
* Constructs a ClassType
. It represents
* the name of a non-nested class.
*
* @param className a fully qualified class name.
* @param args type arguments or null.
*/
public ClassType(String className, TypeArgument[] args) {
name = className;
arguments = args;
}
/**
* Constructs a ClassType
. It represents
* the name of a non-nested class.
*
* @param className a fully qualified class name.
*/
public ClassType(String className) {
this(className, null);
}
/**
* Returns the class name.
*/
public String getName() {
return name;
}
/**
* Returns the type arguments.
*
* @return null if no type arguments are given to this class.
*/
public TypeArgument[] getTypeArguments() { return arguments; }
/**
* If this class is a member of another class, returns the
* class in which this class is declared.
*
* @return null if this class is not a member of another class.
*/
public ClassType getDeclaringClass() { return null; }
/**
* Returns the string representation.
*/
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
ClassType parent = getDeclaringClass();
if (parent != null)
sbuf.append(parent.toString()).append('.');
return toString2(sbuf);
}
private String toString2(StringBuilder sbuf) {
sbuf.append(name);
if (arguments != null) {
sbuf.append('<');
int n = arguments.length;
for (int i = 0; i < n; i++) {
if (i > 0)
sbuf.append(", ");
sbuf.append(arguments[i].toString());
}
sbuf.append('>');
}
return sbuf.toString();
}
/**
* Returns the type name in the JVM internal style.
* For example, if the type is a nested class {@code foo.Bar.Baz},
* then {@code foo.Bar$Baz} is returned.
*/
@Override
public String jvmTypeName() {
StringBuilder sbuf = new StringBuilder();
ClassType parent = getDeclaringClass();
if (parent != null)
sbuf.append(parent.jvmTypeName()).append('$');
return toString2(sbuf);
}
@Override
void encode(StringBuilder sb) {
sb.append('L');
encode2(sb);
sb.append(';');
}
void encode2(StringBuilder sb) {
ClassType parent = getDeclaringClass();
if (parent != null) {
parent.encode2(sb);
sb.append('$');
}
sb.append(name.replace('.', '/'));
if (arguments != null)
TypeArgument.encode(sb, arguments);
}
}
/**
* Nested class types.
*/
public static class NestedClassType extends ClassType {
ClassType parent;
NestedClassType(String s, int b, int e,
TypeArgument[] targs, ClassType p) {
super(s, b, e, targs);
parent = p;
}
/**
* Constructs a NestedClassType
.
*
* @param parent the class surrounding this class type.
* @param className a simple class name. It does not include
* a package name or a parent's class name.
* @param args type parameters or null.
*/
public NestedClassType(ClassType parent, String className, TypeArgument[] args) {
super(className, args);
this.parent = parent;
}
/**
* Returns the class that declares this nested class.
* This nested class is a member of that declaring class.
*/
@Override
public ClassType getDeclaringClass() { return parent; }
}
/**
* Array types.
*/
public static class ArrayType extends ObjectType {
int dim;
Type componentType;
/**
* Constructs an ArrayType
.
*
* @param d dimension.
* @param comp the component type.
*/
public ArrayType(int d, Type comp) {
dim = d;
componentType = comp;
}
/**
* Returns the dimension of the array.
*/
public int getDimension() { return dim; }
/**
* Returns the component type.
*/
public Type getComponentType() {
return componentType;
}
/**
* Returns the string representation.
*/
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder(componentType.toString());
for (int i = 0; i < dim; i++)
sbuf.append("[]");
return sbuf.toString();
}
@Override
void encode(StringBuilder sb) {
for (int i = 0; i < dim; i++)
sb.append('[');
componentType.encode(sb);
}
}
/**
* Type variables.
*/
public static class TypeVariable extends ObjectType {
String name;
TypeVariable(String sig, int begin, int end) {
name = sig.substring(begin, end);
}
/**
* Constructs a TypeVariable
.
*
* @param name the name of a type variable.
*/
public TypeVariable(String name) {
this.name = name;
}
/**
* Returns the variable name.
*/
public String getName() {
return name;
}
/**
* Returns the string representation.
*/
@Override
public String toString() {
return name;
}
@Override
void encode(StringBuilder sb) {
sb.append('T').append(name).append(';');
}
}
/**
* Parses the given signature string as a class signature.
*
* @param sig the signature obtained from the SignatureAttribute
* of a ClassFile
.
* @return a tree-like data structure representing a class signature. It provides
* convenient accessor methods.
* @throws BadBytecode thrown when a syntactical error is found.
* @see #getSignature()
* @since 3.5
*/
public static ClassSignature toClassSignature(String sig) throws BadBytecode {
try {
return parseSig(sig);
}
catch (IndexOutOfBoundsException e) {
throw error(sig);
}
}
/**
* Parses the given signature string as a method type signature.
*
* @param sig the signature obtained from the SignatureAttribute
* of a MethodInfo
.
* @return @return a tree-like data structure representing a method signature. It provides
* convenient accessor methods.
* @throws BadBytecode thrown when a syntactical error is found.
* @see #getSignature()
* @since 3.5
*/
public static MethodSignature toMethodSignature(String sig) throws BadBytecode {
try {
return parseMethodSig(sig);
}
catch (IndexOutOfBoundsException e) {
throw error(sig);
}
}
/**
* Parses the given signature string as a field type signature.
*
* @param sig the signature string obtained from the SignatureAttribute
* of a FieldInfo
.
* @return the field type signature.
* @throws BadBytecode thrown when a syntactical error is found.
* @see #getSignature()
* @since 3.5
*/
public static ObjectType toFieldSignature(String sig) throws BadBytecode {
try {
return parseObjectType(sig, new Cursor(), false);
}
catch (IndexOutOfBoundsException e) {
throw error(sig);
}
}
/**
* Parses the given signature string as a type signature.
* The type signature is either the field type signature or a base type
* descriptor including void
type.
*
* @throws BadBytecode thrown when a syntactical error is found.
* @since 3.18
*/
public static Type toTypeSignature(String sig) throws BadBytecode {
try {
return parseType(sig, new Cursor());
}
catch (IndexOutOfBoundsException e) {
throw error(sig);
}
}
private static ClassSignature parseSig(String sig)
throws BadBytecode, IndexOutOfBoundsException
{
Cursor cur = new Cursor();
TypeParameter[] tp = parseTypeParams(sig, cur);
ClassType superClass = parseClassType(sig, cur);
int sigLen = sig.length();
List ifArray = new ArrayList();
while (cur.position < sigLen && sig.charAt(cur.position) == 'L')
ifArray.add(parseClassType(sig, cur));
ClassType[] ifs
= ifArray.toArray(new ClassType[ifArray.size()]);
return new ClassSignature(tp, superClass, ifs);
}
private static MethodSignature parseMethodSig(String sig)
throws BadBytecode
{
Cursor cur = new Cursor();
TypeParameter[] tp = parseTypeParams(sig, cur);
if (sig.charAt(cur.position++) != '(')
throw error(sig);
List params = new ArrayList();
while (sig.charAt(cur.position) != ')') {
Type t = parseType(sig, cur);
params.add(t);
}
cur.position++;
Type ret = parseType(sig, cur);
int sigLen = sig.length();
List exceptions = new ArrayList();
while (cur.position < sigLen && sig.charAt(cur.position) == '^') {
cur.position++;
ObjectType t = parseObjectType(sig, cur, false);
if (t instanceof ArrayType)
throw error(sig);
exceptions.add(t);
}
Type[] p = params.toArray(new Type[params.size()]);
ObjectType[] ex = exceptions.toArray(new ObjectType[exceptions.size()]);
return new MethodSignature(tp, p, ret, ex);
}
private static TypeParameter[] parseTypeParams(String sig, Cursor cur)
throws BadBytecode
{
List typeParam = new ArrayList();
if (sig.charAt(cur.position) == '<') {
cur.position++;
while (sig.charAt(cur.position) != '>') {
int nameBegin = cur.position;
int nameEnd = cur.indexOf(sig, ':');
ObjectType classBound = parseObjectType(sig, cur, true);
List ifBound = new ArrayList();
while (sig.charAt(cur.position) == ':') {
cur.position++;
ObjectType t = parseObjectType(sig, cur, false);
ifBound.add(t);
}
TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd,
classBound, ifBound.toArray(new ObjectType[ifBound.size()]));
typeParam.add(p);
}
cur.position++;
}
return typeParam.toArray(new TypeParameter[typeParam.size()]);
}
private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow)
throws BadBytecode
{
int i;
int begin = c.position;
switch (sig.charAt(begin)) {
case 'L' :
return parseClassType2(sig, c, null);
case 'T' :
i = c.indexOf(sig, ';');
return new TypeVariable(sig, begin + 1, i);
case '[' :
return parseArray(sig, c);
default :
if (dontThrow)
return null;
throw error(sig);
}
}
private static ClassType parseClassType(String sig, Cursor c)
throws BadBytecode
{
if (sig.charAt(c.position) == 'L')
return parseClassType2(sig, c, null);
throw error(sig);
}
private static ClassType parseClassType2(String sig, Cursor c, ClassType parent)
throws BadBytecode
{
int start = ++c.position;
char t;
do {
t = sig.charAt(c.position++);
} while (t != '$' && t != '<' && t != ';');
int end = c.position - 1;
TypeArgument[] targs;
if (t == '<') {
targs = parseTypeArgs(sig, c);
t = sig.charAt(c.position++);
}
else
targs = null;
ClassType thisClass = ClassType.make(sig, start, end, targs, parent);
if (t == '$' || t == '.') {
c.position--;
return parseClassType2(sig, c, thisClass);
}
return thisClass;
}
private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode {
List args = new ArrayList();
char t;
while ((t = sig.charAt(c.position++)) != '>') {
TypeArgument ta;
if (t == '*' )
ta = new TypeArgument(null, '*');
else {
if (t != '+' && t != '-') {
t = ' ';
c.position--;
}
ta = new TypeArgument(parseObjectType(sig, c, false), t);
}
args.add(ta);
}
return args.toArray(new TypeArgument[args.size()]);
}
private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode {
int dim = 1;
while (sig.charAt(++c.position) == '[')
dim++;
return new ArrayType(dim, parseType(sig, c));
}
private static Type parseType(String sig, Cursor c) throws BadBytecode {
Type t = parseObjectType(sig, c, true);
if (t == null)
t = new BaseType(sig.charAt(c.position++));
return t;
}
private static BadBytecode error(String sig) {
return new BadBytecode("bad signature: " + sig);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy