javassist.compiler.MemberResolver Maven / Gradle / Ivy
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 javassist.compiler;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.ClassFile;
import javassist.bytecode.Descriptor;
import javassist.bytecode.MethodInfo;
import javassist.compiler.ast.ASTList;
import javassist.compiler.ast.ASTree;
import javassist.compiler.ast.Declarator;
import javassist.compiler.ast.Keyword;
import javassist.compiler.ast.Symbol;
/* Code generator methods depending on javassist.* classes.
*/
public class MemberResolver implements TokenId {
private ClassPool classPool;
public MemberResolver(ClassPool cp) {
classPool = cp;
}
public ClassPool getClassPool() { return classPool; }
private static void fatal() throws CompileError {
throw new CompileError("fatal");
}
public static class Method {
public CtClass declaring;
public MethodInfo info;
public int notmatch;
public Method(CtClass c, MethodInfo i, int n) {
declaring = c;
info = i;
notmatch = n;
}
/**
* Returns true if the invoked method is static.
*/
public boolean isStatic() {
int acc = info.getAccessFlags();
return (acc & AccessFlag.STATIC) != 0;
}
}
public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current,
String methodName,
int[] argTypes, int[] argDims,
String[] argClassNames)
throws CompileError
{
Method maybe = null;
// to enable the creation of a recursively called method
if (current != null && clazz == currentClass)
if (current.getName().equals(methodName)) {
int res = compareSignature(current.getDescriptor(),
argTypes, argDims, argClassNames);
if (res != NO) {
Method r = new Method(clazz, current, res);
if (res == YES)
return r;
maybe = r;
}
}
Method m = lookupMethod(clazz, methodName, argTypes, argDims,
argClassNames, maybe != null);
if (m != null)
return m;
return maybe;
}
private Method lookupMethod(CtClass clazz, String methodName,
int[] argTypes, int[] argDims,
String[] argClassNames, boolean onlyExact)
throws CompileError
{
Method maybe = null;
ClassFile cf = clazz.getClassFile2();
// If the class is an array type, the class file is null.
// If so, search the super class java.lang.Object for clone() etc.
if (cf != null) {
List list = cf.getMethods();
for (MethodInfo minfo:list) {
if (minfo.getName().equals(methodName)
&& (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0) {
int res = compareSignature(minfo.getDescriptor(),
argTypes, argDims, argClassNames);
if (res != NO) {
Method r = new Method(clazz, minfo, res);
if (res == YES)
return r;
else if (maybe == null || maybe.notmatch > res)
maybe = r;
}
}
}
}
if (onlyExact)
maybe = null;
else
if (maybe != null)
return maybe;
int mod = clazz.getModifiers();
boolean isIntf = Modifier.isInterface(mod);
try {
// skip searching java.lang.Object if clazz is an interface type.
if (!isIntf) {
CtClass pclazz = clazz.getSuperclass();
if (pclazz != null) {
Method r = lookupMethod(pclazz, methodName, argTypes,
argDims, argClassNames, onlyExact);
if (r != null)
return r;
}
}
}
catch (NotFoundException e) {}
try {
CtClass[] ifs = clazz.getInterfaces();
for (CtClass intf:ifs) {
Method r = lookupMethod(intf, methodName,
argTypes, argDims, argClassNames,
onlyExact);
if (r != null)
return r;
}
if (isIntf) {
// finally search java.lang.Object.
CtClass pclazz = clazz.getSuperclass();
if (pclazz != null) {
Method r = lookupMethod(pclazz, methodName, argTypes,
argDims, argClassNames, onlyExact);
if (r != null)
return r;
}
}
}
catch (NotFoundException e) {}
return maybe;
}
private static final int YES = 0;
private static final int NO = -1;
/*
* Returns YES if actual parameter types matches the given signature.
*
* argTypes, argDims, and argClassNames represent actual parameters.
*
* This method does not correctly implement the Java method dispatch
* algorithm.
*
* If some of the parameter types exactly match but others are subtypes of
* the corresponding type in the signature, this method returns the number
* of parameter types that do not exactly match.
*/
private int compareSignature(String desc, int[] argTypes,
int[] argDims, String[] argClassNames)
throws CompileError
{
int result = YES;
int i = 1;
int nArgs = argTypes.length;
if (nArgs != Descriptor.numOfParameters(desc))
return NO;
int len = desc.length();
for (int n = 0; i < len; ++n) {
char c = desc.charAt(i++);
if (c == ')')
return (n == nArgs ? result : NO);
else if (n >= nArgs)
return NO;
int dim = 0;
while (c == '[') {
++dim;
c = desc.charAt(i++);
}
if (argTypes[n] == NULL) {
if (dim == 0 && c != 'L')
return NO;
if (c == 'L')
i = desc.indexOf(';', i) + 1;
}
else if (argDims[n] != dim) {
if (!(dim == 0 && c == 'L'
&& desc.startsWith("java/lang/Object;", i)))
return NO;
// if the thread reaches here, c must be 'L'.
i = desc.indexOf(';', i) + 1;
result++;
if (i <= 0)
return NO; // invalid descriptor?
}
else if (c == 'L') { // not compare
int j = desc.indexOf(';', i);
if (j < 0 || argTypes[n] != CLASS)
return NO;
String cname = desc.substring(i, j);
if (!cname.equals(argClassNames[n])) {
CtClass clazz = lookupClassByJvmName(argClassNames[n]);
try {
if (clazz.subtypeOf(lookupClassByJvmName(cname)))
result++;
else
return NO;
}
catch (NotFoundException e) {
result++; // should be NO?
}
}
i = j + 1;
}
else {
int t = descToType(c);
int at = argTypes[n];
if (t != at)
if (t == INT
&& (at == SHORT || at == BYTE || at == CHAR))
result++;
else
return NO;
}
}
return NO;
}
/**
* Only used by fieldAccess() in MemberCodeGen and TypeChecker.
*
* @param jvmClassName a JVM class name. e.g. java/lang/String
* @see #lookupClass(String, boolean)
*/
public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym,
ASTree expr) throws NoFieldException
{
String field = fieldSym.get();
CtClass cc = null;
try {
cc = lookupClass(jvmToJavaName(jvmClassName), true);
}
catch (CompileError e) {
// EXPR might be part of a qualified class name.
throw new NoFieldException(jvmClassName + "/" + field, expr);
}
try {
return cc.getField(field);
}
catch (NotFoundException e) {
// maybe an inner class.
jvmClassName = javaToJvmName(cc.getName());
throw new NoFieldException(jvmClassName + "$" + field, expr);
}
}
/**
* @param jvmClassName a JVM class name. e.g. java/lang/String
*/
public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName)
throws CompileError
{
return lookupField(jvmToJavaName(jvmClassName), fieldName);
}
/**
* @param className a qualified class name. e.g. java.lang.String
*/
public CtField lookupField(String className, Symbol fieldName)
throws CompileError
{
CtClass cc = lookupClass(className, false);
try {
return cc.getField(fieldName.get());
}
catch (NotFoundException e) {}
throw new CompileError("no such field: " + fieldName.get());
}
public CtClass lookupClassByName(ASTList name) throws CompileError {
return lookupClass(Declarator.astToClassName(name, '.'), false);
}
public CtClass lookupClassByJvmName(String jvmName) throws CompileError {
return lookupClass(jvmToJavaName(jvmName), false);
}
public CtClass lookupClass(Declarator decl) throws CompileError {
return lookupClass(decl.getType(), decl.getArrayDim(),
decl.getClassName());
}
/**
* @param classname jvm class name.
*/
public CtClass lookupClass(int type, int dim, String classname)
throws CompileError
{
String cname = "";
CtClass clazz;
if (type == CLASS) {
clazz = lookupClassByJvmName(classname);
if (dim > 0)
cname = clazz.getName();
else
return clazz;
}
else
cname = getTypeName(type);
while (dim-- > 0)
cname += "[]";
return lookupClass(cname, false);
}
/*
* type cannot be CLASS
*/
static String getTypeName(int type) throws CompileError {
String cname = "";
switch (type) {
case BOOLEAN :
cname = "boolean";
break;
case CHAR :
cname = "char";
break;
case BYTE :
cname = "byte";
break;
case SHORT :
cname = "short";
break;
case INT :
cname = "int";
break;
case LONG :
cname = "long";
break;
case FLOAT :
cname = "float";
break;
case DOUBLE :
cname = "double";
break;
case VOID :
cname = "void";
break;
default :
fatal();
}
return cname;
}
/**
* @param name a qualified class name. e.g. java.lang.String
*/
public CtClass lookupClass(String name, boolean notCheckInner)
throws CompileError
{
Map cache = getInvalidNames();
String found = cache.get(name);
if (found == INVALID)
throw new CompileError("no such class: " + name);
else if (found != null)
try {
return classPool.get(found);
}
catch (NotFoundException e) {}
CtClass cc = null;
try {
cc = lookupClass0(name, notCheckInner);
}
catch (NotFoundException e) {
cc = searchImports(name);
}
cache.put(name, cc.getName());
return cc;
}
private static final String INVALID = "";
private static Map>> invalidNamesMap =
new WeakHashMap>>();
private Map invalidNames = null;
// for unit tests
public static int getInvalidMapSize() { return invalidNamesMap.size(); }
private Map getInvalidNames() {
Map ht = invalidNames;
if (ht == null) {
synchronized (MemberResolver.class) {
Reference