001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.compiler;
017:
018: import java.util.List;
019: import java.util.Iterator;
020: import javassist.*;
021: import javassist.bytecode.*;
022: import javassist.compiler.ast.*;
023:
024: /* Code generator methods depending on javassist.* classes.
025: */
026: public class MemberResolver implements TokenId {
027: private ClassPool classPool;
028:
029: public MemberResolver(ClassPool cp) {
030: classPool = cp;
031: }
032:
033: public ClassPool getClassPool() {
034: return classPool;
035: }
036:
037: private static void fatal() throws CompileError {
038: throw new CompileError("fatal");
039: }
040:
041: /**
042: * @param jvmClassName a class name. Not a package name.
043: */
044: public void recordPackage(String jvmClassName) {
045: String classname = jvmToJavaName(jvmClassName);
046: for (;;) {
047: int i = classname.lastIndexOf('.');
048: if (i > 0) {
049: classname = classname.substring(0, i);
050: classPool.recordInvalidClassName(classname);
051: } else
052: break;
053: }
054: }
055:
056: public static class Method {
057: public CtClass declaring;
058: public MethodInfo info;
059: public int notmatch;
060:
061: public Method(CtClass c, MethodInfo i, int n) {
062: declaring = c;
063: info = i;
064: notmatch = n;
065: }
066:
067: /**
068: * Returns true if the invoked method is static.
069: */
070: public boolean isStatic() {
071: int acc = info.getAccessFlags();
072: return (acc & AccessFlag.STATIC) != 0;
073: }
074: }
075:
076: public Method lookupMethod(CtClass clazz, CtClass currentClass,
077: MethodInfo current, String methodName, int[] argTypes,
078: int[] argDims, String[] argClassNames) throws CompileError {
079: Method maybe = null;
080: // to enable the creation of a recursively called method
081: if (current != null && clazz == currentClass)
082: if (current.getName().equals(methodName)) {
083: int res = compareSignature(current.getDescriptor(),
084: argTypes, argDims, argClassNames);
085: if (res != NO) {
086: Method r = new Method(clazz, current, res);
087: if (res == YES)
088: return r;
089: else
090: maybe = r;
091: }
092: }
093:
094: Method m = lookupMethod(clazz, methodName, argTypes, argDims,
095: argClassNames, maybe != null);
096: if (m != null)
097: return m;
098: else
099: return maybe;
100: }
101:
102: private Method lookupMethod(CtClass clazz, String methodName,
103: int[] argTypes, int[] argDims, String[] argClassNames,
104: boolean onlyExact) throws CompileError {
105: Method maybe = null;
106: List list = clazz.getClassFile2().getMethods();
107: int n = list.size();
108: for (int i = 0; i < n; ++i) {
109: MethodInfo minfo = (MethodInfo) list.get(i);
110: if (minfo.getName().equals(methodName)) {
111: int res = compareSignature(minfo.getDescriptor(),
112: argTypes, argDims, argClassNames);
113: if (res != NO) {
114: Method r = new Method(clazz, minfo, res);
115: if (res == YES)
116: return r;
117: else if (maybe == null || maybe.notmatch > res)
118: maybe = r;
119: }
120: }
121: }
122:
123: if (onlyExact)
124: maybe = null;
125: else
126: onlyExact = maybe != null;
127:
128: try {
129: CtClass pclazz = clazz.getSuperclass();
130: if (pclazz != null) {
131: Method r = lookupMethod(pclazz, methodName, argTypes,
132: argDims, argClassNames, onlyExact);
133: if (r != null)
134: return r;
135: }
136: } catch (NotFoundException e) {
137: }
138:
139: int mod = clazz.getModifiers();
140: if (Modifier.isAbstract(mod) || Modifier.isInterface(mod))
141: try {
142: CtClass[] ifs = clazz.getInterfaces();
143: int size = ifs.length;
144: for (int i = 0; i < size; ++i) {
145: Method r = lookupMethod(ifs[i], methodName,
146: argTypes, argDims, argClassNames, onlyExact);
147: if (r != null)
148: return r;
149: }
150: } catch (NotFoundException e) {
151: }
152:
153: return maybe;
154: }
155:
156: private static final int YES = 0;
157: private static final int NO = -1;
158:
159: /*
160: * Returns YES if actual parameter types matches the given signature.
161: *
162: * argTypes, argDims, and argClassNames represent actual parameters.
163: *
164: * This method does not correctly implement the Java method dispatch
165: * algorithm.
166: *
167: * If some of the parameter types exactly match but others are subtypes of
168: * the corresponding type in the signature, this method returns the number
169: * of parameter types that do not exactly match.
170: */
171: private int compareSignature(String desc, int[] argTypes,
172: int[] argDims, String[] argClassNames) throws CompileError {
173: int result = YES;
174: int i = 1;
175: int nArgs = argTypes.length;
176: if (nArgs != Descriptor.numOfParameters(desc))
177: return NO;
178:
179: int len = desc.length();
180: for (int n = 0; i < len; ++n) {
181: char c = desc.charAt(i++);
182: if (c == ')')
183: return (n == nArgs ? result : NO);
184: else if (n >= nArgs)
185: return NO;
186:
187: int dim = 0;
188: while (c == '[') {
189: ++dim;
190: c = desc.charAt(i++);
191: }
192:
193: if (argTypes[n] == NULL) {
194: if (dim == 0 && c != 'L')
195: return NO;
196:
197: if (c == 'L')
198: i = desc.indexOf(';', i) + 1;
199: } else if (argDims[n] != dim) {
200: if (!(dim == 0 && c == 'L' && desc.startsWith(
201: "java/lang/Object;", i)))
202: return NO;
203:
204: // if the thread reaches here, c must be 'L'.
205: i = desc.indexOf(';', i) + 1;
206: result++;
207: if (i <= 0)
208: return NO; // invalid descriptor?
209: } else if (c == 'L') { // not compare
210: int j = desc.indexOf(';', i);
211: if (j < 0 || argTypes[n] != CLASS)
212: return NO;
213:
214: String cname = desc.substring(i, j);
215: if (!cname.equals(argClassNames[n])) {
216: CtClass clazz = lookupClassByJvmName(argClassNames[n]);
217: try {
218: if (clazz
219: .subtypeOf(lookupClassByJvmName(cname)))
220: result++;
221: else
222: return NO;
223: } catch (NotFoundException e) {
224: result++; // should be NO?
225: }
226: }
227:
228: i = j + 1;
229: } else {
230: int t = descToType(c);
231: int at = argTypes[n];
232: if (t != at)
233: if (t == INT
234: && (at == SHORT || at == BYTE || at == CHAR))
235: result++;
236: else
237: return NO;
238: }
239: }
240:
241: return NO;
242: }
243:
244: /**
245: * Only used by fieldAccess() in MemberCodeGen and TypeChecker.
246: *
247: * @param jvmClassName a JVM class name. e.g. java/lang/String
248: */
249: public CtField lookupFieldByJvmName2(String jvmClassName,
250: Symbol fieldSym, ASTree expr) throws NoFieldException {
251: String field = fieldSym.get();
252: CtClass cc = null;
253: try {
254: cc = lookupClass(jvmToJavaName(jvmClassName), true);
255: } catch (CompileError e) {
256: // EXPR might be part of a qualified class name.
257: throw new NoFieldException(jvmClassName + "/" + field, expr);
258: }
259:
260: try {
261: return cc.getField(field);
262: } catch (NotFoundException e) {
263: // maybe an inner class.
264: jvmClassName = javaToJvmName(cc.getName());
265: throw new NoFieldException(jvmClassName + "$" + field, expr);
266: }
267: }
268:
269: /**
270: * @param jvmClassName a JVM class name. e.g. java/lang/String
271: */
272: public CtField lookupFieldByJvmName(String jvmClassName,
273: Symbol fieldName) throws CompileError {
274: return lookupField(jvmToJavaName(jvmClassName), fieldName);
275: }
276:
277: /**
278: * @param name a qualified class name. e.g. java.lang.String
279: */
280: public CtField lookupField(String className, Symbol fieldName)
281: throws CompileError {
282: CtClass cc = lookupClass(className, false);
283: try {
284: return cc.getField(fieldName.get());
285: } catch (NotFoundException e) {
286: }
287: throw new CompileError("no such field: " + fieldName.get());
288: }
289:
290: public CtClass lookupClassByName(ASTList name) throws CompileError {
291: return lookupClass(Declarator.astToClassName(name, '.'), false);
292: }
293:
294: public CtClass lookupClassByJvmName(String jvmName)
295: throws CompileError {
296: return lookupClass(jvmToJavaName(jvmName), false);
297: }
298:
299: public CtClass lookupClass(Declarator decl) throws CompileError {
300: return lookupClass(decl.getType(), decl.getArrayDim(), decl
301: .getClassName());
302: }
303:
304: /**
305: * @parma classname jvm class name.
306: */
307: public CtClass lookupClass(int type, int dim, String classname)
308: throws CompileError {
309: String cname = "";
310: CtClass clazz;
311: if (type == CLASS) {
312: clazz = lookupClassByJvmName(classname);
313: if (dim > 0)
314: cname = clazz.getName();
315: else
316: return clazz;
317: } else
318: cname = getTypeName(type);
319:
320: while (dim-- > 0)
321: cname += "[]";
322:
323: return lookupClass(cname, false);
324: }
325:
326: /*
327: * type cannot be CLASS
328: */
329: static String getTypeName(int type) throws CompileError {
330: String cname = "";
331: switch (type) {
332: case BOOLEAN:
333: cname = "boolean";
334: break;
335: case CHAR:
336: cname = "char";
337: break;
338: case BYTE:
339: cname = "byte";
340: break;
341: case SHORT:
342: cname = "short";
343: break;
344: case INT:
345: cname = "int";
346: break;
347: case LONG:
348: cname = "long";
349: break;
350: case FLOAT:
351: cname = "float";
352: break;
353: case DOUBLE:
354: cname = "double";
355: break;
356: case VOID:
357: cname = "void";
358: break;
359: default:
360: fatal();
361: }
362:
363: return cname;
364: }
365:
366: /**
367: * @param name a qualified class name. e.g. java.lang.String
368: */
369: public CtClass lookupClass(String name, boolean notCheckInner)
370: throws CompileError {
371: try {
372: return lookupClass0(name, notCheckInner);
373: } catch (NotFoundException e) {
374: return searchImports(name);
375: }
376: }
377:
378: private CtClass searchImports(String orgName) throws CompileError {
379: if (orgName.indexOf('.') < 0) {
380: Iterator it = classPool.getImportedPackages();
381: while (it.hasNext()) {
382: String pac = (String) it.next();
383: String fqName = pac + '.' + orgName;
384: try {
385: CtClass cc = classPool.get(fqName);
386: // if the class is found,
387: classPool.recordInvalidClassName(orgName);
388: return cc;
389: } catch (NotFoundException e) {
390: classPool.recordInvalidClassName(fqName);
391: }
392: }
393: }
394:
395: throw new CompileError("no such class: " + orgName);
396: }
397:
398: private CtClass lookupClass0(String classname, boolean notCheckInner)
399: throws NotFoundException {
400: CtClass cc = null;
401: do {
402: try {
403: cc = classPool.get(classname);
404: } catch (NotFoundException e) {
405: int i = classname.lastIndexOf('.');
406: if (notCheckInner || i < 0)
407: throw e;
408: else {
409: StringBuffer sbuf = new StringBuffer(classname);
410: sbuf.setCharAt(i, '$');
411: classname = sbuf.toString();
412: }
413: }
414: } while (cc == null);
415: return cc;
416: }
417:
418: /* Converts a class name into a JVM-internal representation.
419: *
420: * It may also expand a simple class name to java.lang.*.
421: * For example, this converts Object into java/lang/Object.
422: */
423: public String resolveClassName(ASTList name) throws CompileError {
424: if (name == null)
425: return null;
426: else
427: return javaToJvmName(lookupClassByName(name).getName());
428: }
429:
430: /* Expands a simple class name to java.lang.*.
431: * For example, this converts Object into java/lang/Object.
432: */
433: public String resolveJvmClassName(String jvmName)
434: throws CompileError {
435: if (jvmName == null)
436: return null;
437: else
438: return javaToJvmName(lookupClassByJvmName(jvmName)
439: .getName());
440: }
441:
442: public static CtClass getSuperclass(CtClass c) throws CompileError {
443: try {
444: CtClass sc = c.getSuperclass();
445: if (sc != null)
446: return sc;
447: } catch (NotFoundException e) {
448: }
449: throw new CompileError("cannot find the super class of "
450: + c.getName());
451: }
452:
453: public static String javaToJvmName(String classname) {
454: return classname.replace('.', '/');
455: }
456:
457: public static String jvmToJavaName(String classname) {
458: return classname.replace('/', '.');
459: }
460:
461: public static int descToType(char c) throws CompileError {
462: switch (c) {
463: case 'Z':
464: return BOOLEAN;
465: case 'C':
466: return CHAR;
467: case 'B':
468: return BYTE;
469: case 'S':
470: return SHORT;
471: case 'I':
472: return INT;
473: case 'J':
474: return LONG;
475: case 'F':
476: return FLOAT;
477: case 'D':
478: return DOUBLE;
479: case 'V':
480: return VOID;
481: case 'L':
482: case '[':
483: return CLASS;
484: default:
485: fatal();
486: return VOID; // never reach here
487: }
488: }
489:
490: public static int getModifiers(ASTList mods) {
491: int m = 0;
492: while (mods != null) {
493: Keyword k = (Keyword) mods.head();
494: mods = mods.tail();
495: switch (k.get()) {
496: case STATIC:
497: m |= Modifier.STATIC;
498: break;
499: case FINAL:
500: m |= Modifier.FINAL;
501: break;
502: case SYNCHRONIZED:
503: m |= Modifier.SYNCHRONIZED;
504: break;
505: case ABSTRACT:
506: m |= Modifier.ABSTRACT;
507: break;
508: case PUBLIC:
509: m |= Modifier.PUBLIC;
510: break;
511: case PROTECTED:
512: m |= Modifier.PROTECTED;
513: break;
514: case PRIVATE:
515: m |= Modifier.PRIVATE;
516: break;
517: case VOLATILE:
518: m |= Modifier.VOLATILE;
519: break;
520: case TRANSIENT:
521: m |= Modifier.TRANSIENT;
522: break;
523: case STRICT:
524: m |= Modifier.STRICT;
525: break;
526: }
527: }
528:
529: return m;
530: }
531: }
|