123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- /*
- * 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<MethodInfo> 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 maybe super class has more precise match
-
- 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) {
- if (maybe == null || maybe.notmatch > r.notmatch) {
- maybe = 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) {
- if (maybe == null || maybe.notmatch > r.notmatch) {
- maybe = 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) {
- if (maybe == null || maybe.notmatch > r.notmatch) {
- maybe = 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<String,String> 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 = "<invalid>";
- private static Map<ClassPool, Reference<Map<String,String>>> invalidNamesMap =
- new WeakHashMap<ClassPool, Reference<Map<String,String>>>();
- private Map<String,String> invalidNames = null;
-
- // for unit tests
- public static int getInvalidMapSize() { return invalidNamesMap.size(); }
-
- private Map<String,String> getInvalidNames() {
- Map<String,String> ht = invalidNames;
- if (ht == null) {
- synchronized (MemberResolver.class) {
- Reference<Map<String,String>> ref = invalidNamesMap.get(classPool);
- if (ref != null)
- ht = ref.get();
-
- if (ht == null) {
- ht = new Hashtable<String,String>();
- invalidNamesMap.put(classPool, new WeakReference<Map<String,String>>(ht));
- }
- }
-
- invalidNames = ht;
- }
-
- return ht;
- }
-
- private CtClass searchImports(String orgName)
- throws CompileError
- {
- if (orgName.indexOf('.') < 0) {
- Iterator<String> it = classPool.getImportedPackages();
- while (it.hasNext()) {
- String pac = it.next();
- String fqName = pac.replaceAll("\\.$","") + "." + orgName;
- try {
- return classPool.get(fqName);
- }
- catch (NotFoundException e) {
- try {
- if (pac.endsWith("." + orgName))
- return classPool.get(pac);
- }
- catch (NotFoundException e2) {}
- }
- }
- }
-
- getInvalidNames().put(orgName, INVALID);
- throw new CompileError("no such class: " + orgName);
- }
-
- private CtClass lookupClass0(String classname, boolean notCheckInner)
- throws NotFoundException
- {
- CtClass cc = null;
- do {
- try {
- cc = classPool.get(classname);
- }
- catch (NotFoundException e) {
- int i = classname.lastIndexOf('.');
- if (notCheckInner || i < 0)
- throw e;
- StringBuilder sbuf = new StringBuilder(classname);
- sbuf.setCharAt(i, '$');
- classname = sbuf.toString();
- }
- } while (cc == null);
- return cc;
- }
-
- /* Converts a class name into a JVM-internal representation.
- *
- * It may also expand a simple class name to java.lang.*.
- * For example, this converts Object into java/lang/Object.
- */
- public String resolveClassName(ASTList name) throws CompileError {
- if (name == null)
- return null;
- return javaToJvmName(lookupClassByName(name).getName());
- }
-
- /* Expands a simple class name to java.lang.*.
- * For example, this converts Object into java/lang/Object.
- */
- public String resolveJvmClassName(String jvmName) throws CompileError {
- if (jvmName == null)
- return null;
- return javaToJvmName(lookupClassByJvmName(jvmName).getName());
- }
-
- public static CtClass getSuperclass(CtClass c) throws CompileError {
- try {
- CtClass sc = c.getSuperclass();
- if (sc != null)
- return sc;
- }
- catch (NotFoundException e) {}
- throw new CompileError("cannot find the super class of "
- + c.getName());
- }
-
- public static CtClass getSuperInterface(CtClass c, String interfaceName)
- throws CompileError
- {
- try {
- CtClass[] intfs = c.getInterfaces();
- for (int i = 0; i < intfs.length; i++)
- if (intfs[i].getName().equals(interfaceName))
- return intfs[i];
- } catch (NotFoundException e) {}
- throw new CompileError("cannot find the super interface " + interfaceName
- + " of " + c.getName());
- }
-
- public static String javaToJvmName(String classname) {
- return classname.replace('.', '/');
- }
-
- public static String jvmToJavaName(String classname) {
- return classname.replace('/', '.');
- }
-
- public static int descToType(char c) throws CompileError {
- switch (c) {
- case 'Z' :
- return BOOLEAN;
- case 'C' :
- return CHAR;
- case 'B' :
- return BYTE;
- case 'S' :
- return SHORT;
- case 'I' :
- return INT;
- case 'J' :
- return LONG;
- case 'F' :
- return FLOAT;
- case 'D' :
- return DOUBLE;
- case 'V' :
- return VOID;
- case 'L' :
- case '[' :
- return CLASS;
- default :
- fatal();
- return VOID; // never reach here
- }
- }
-
- public static int getModifiers(ASTList mods) {
- int m = 0;
- while (mods != null) {
- Keyword k = (Keyword)mods.head();
- mods = mods.tail();
- switch (k.get()) {
- case STATIC :
- m |= Modifier.STATIC;
- break;
- case FINAL :
- m |= Modifier.FINAL;
- break;
- case SYNCHRONIZED :
- m |= Modifier.SYNCHRONIZED;
- break;
- case ABSTRACT :
- m |= Modifier.ABSTRACT;
- break;
- case PUBLIC :
- m |= Modifier.PUBLIC;
- break;
- case PROTECTED :
- m |= Modifier.PROTECTED;
- break;
- case PRIVATE :
- m |= Modifier.PRIVATE;
- break;
- case VOLATILE :
- m |= Modifier.VOLATILE;
- break;
- case TRANSIENT :
- m |= Modifier.TRANSIENT;
- break;
- case STRICT :
- m |= Modifier.STRICT;
- break;
- }
- }
-
- return m;
- }
- }
|