com.feilong.lib.javassist.compiler.MemberResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* 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 com.feilong.lib.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 com.feilong.lib.javassist.ClassPool;
import com.feilong.lib.javassist.CtClass;
import com.feilong.lib.javassist.CtField;
import com.feilong.lib.javassist.Modifier;
import com.feilong.lib.javassist.NotFoundException;
import com.feilong.lib.javassist.bytecode.AccessFlag;
import com.feilong.lib.javassist.bytecode.ClassFile;
import com.feilong.lib.javassist.bytecode.Descriptor;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.compiler.ast.ASTList;
import com.feilong.lib.javassist.compiler.ast.ASTree;
import com.feilong.lib.javassist.compiler.ast.Declarator;
import com.feilong.lib.javassist.compiler.ast.Keyword;
import com.feilong.lib.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