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 javassist.CtClass;
019: import javassist.CtField;
020: import javassist.ClassPool;
021: import javassist.Modifier;
022: import javassist.NotFoundException;
023: import javassist.compiler.ast.*;
024: import javassist.bytecode.*;
025:
026: public class TypeChecker extends Visitor implements Opcode, TokenId {
027: static final String javaLangObject = "java.lang.Object";
028: static final String jvmJavaLangObject = "java/lang/Object";
029: static final String jvmJavaLangString = "java/lang/String";
030: static final String jvmJavaLangClass = "java/lang/Class";
031:
032: /* The following fields are used by atXXX() methods
033: * for returning the type of the compiled expression.
034: */
035: protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
036: protected int arrayDim;
037: protected String className; // JVM-internal representation
038:
039: protected MemberResolver resolver;
040: protected CtClass this Class;
041: protected MethodInfo this Method;
042:
043: public TypeChecker(CtClass cc, ClassPool cp) {
044: resolver = new MemberResolver(cp);
045: this Class = cc;
046: this Method = null;
047: }
048:
049: /*
050: * Converts an array of tuples of exprType, arrayDim, and className
051: * into a String object.
052: */
053: protected static String argTypesToString(int[] types, int[] dims,
054: String[] cnames) {
055: StringBuffer sbuf = new StringBuffer();
056: sbuf.append('(');
057: int n = types.length;
058: if (n > 0) {
059: int i = 0;
060: while (true) {
061: typeToString(sbuf, types[i], dims[i], cnames[i]);
062: if (++i < n)
063: sbuf.append(',');
064: else
065: break;
066: }
067: }
068:
069: sbuf.append(')');
070: return sbuf.toString();
071: }
072:
073: /*
074: * Converts a tuple of exprType, arrayDim, and className
075: * into a String object.
076: */
077: protected static StringBuffer typeToString(StringBuffer sbuf,
078: int type, int dim, String cname) {
079: String s;
080: if (type == CLASS)
081: s = MemberResolver.jvmToJavaName(cname);
082: else if (type == NULL)
083: s = "Object";
084: else
085: try {
086: s = MemberResolver.getTypeName(type);
087: } catch (CompileError e) {
088: s = "?";
089: }
090:
091: sbuf.append(s);
092: while (dim-- > 0)
093: sbuf.append("[]");
094:
095: return sbuf;
096: }
097:
098: /**
099: * Records the currently compiled method.
100: */
101: public void setThisMethod(MethodInfo m) {
102: this Method = m;
103: }
104:
105: protected static void fatal() throws CompileError {
106: throw new CompileError("fatal");
107: }
108:
109: /**
110: * Returns the JVM-internal representation of this class name.
111: */
112: protected String getThisName() {
113: return MemberResolver.javaToJvmName(this Class.getName());
114: }
115:
116: /**
117: * Returns the JVM-internal representation of this super class name.
118: */
119: protected String getSuperName() throws CompileError {
120: return MemberResolver.javaToJvmName(MemberResolver
121: .getSuperclass(this Class).getName());
122: }
123:
124: /* Converts a class name into a JVM-internal representation.
125: *
126: * It may also expand a simple class name to java.lang.*.
127: * For example, this converts Object into java/lang/Object.
128: */
129: protected String resolveClassName(ASTList name) throws CompileError {
130: return resolver.resolveClassName(name);
131: }
132:
133: /* Expands a simple class name to java.lang.*.
134: * For example, this converts Object into java/lang/Object.
135: */
136: protected String resolveClassName(String jvmName)
137: throws CompileError {
138: return resolver.resolveJvmClassName(jvmName);
139: }
140:
141: public void atNewExpr(NewExpr expr) throws CompileError {
142: if (expr.isArray())
143: atNewArrayExpr(expr);
144: else {
145: CtClass clazz = resolver.lookupClassByName(expr
146: .getClassName());
147: String cname = clazz.getName();
148: ASTList args = expr.getArguments();
149: atMethodCallCore(clazz, MethodInfo.nameInit, args);
150: exprType = CLASS;
151: arrayDim = 0;
152: className = MemberResolver.javaToJvmName(cname);
153: }
154: }
155:
156: public void atNewArrayExpr(NewExpr expr) throws CompileError {
157: int type = expr.getArrayType();
158: ASTList size = expr.getArraySize();
159: ASTList classname = expr.getClassName();
160: ASTree init = expr.getInitializer();
161: if (init != null)
162: init.accept(this );
163:
164: if (size.length() > 1)
165: atMultiNewArray(type, classname, size);
166: else {
167: ASTree sizeExpr = size.head();
168: if (sizeExpr != null)
169: sizeExpr.accept(this );
170:
171: exprType = type;
172: arrayDim = 1;
173: if (type == CLASS)
174: className = resolveClassName(classname);
175: else
176: className = null;
177: }
178: }
179:
180: public void atArrayInit(ArrayInit init) throws CompileError {
181: ASTList list = init;
182: while (list != null) {
183: ASTree h = list.head();
184: list = list.tail();
185: if (h != null)
186: h.accept(this );
187: }
188: }
189:
190: protected void atMultiNewArray(int type, ASTList classname,
191: ASTList size) throws CompileError {
192: int count, dim;
193: dim = size.length();
194: for (count = 0; size != null; size = size.tail()) {
195: ASTree s = size.head();
196: if (s == null)
197: break; // int[][][] a = new int[3][4][];
198:
199: ++count;
200: s.accept(this );
201: }
202:
203: exprType = type;
204: arrayDim = dim;
205: if (type == CLASS)
206: className = resolveClassName(classname);
207: else
208: className = null;
209: }
210:
211: public void atAssignExpr(AssignExpr expr) throws CompileError {
212: // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
213: int op = expr.getOperator();
214: ASTree left = expr.oprand1();
215: ASTree right = expr.oprand2();
216: if (left instanceof Variable)
217: atVariableAssign(expr, op, (Variable) left,
218: ((Variable) left).getDeclarator(), right);
219: else {
220: if (left instanceof Expr) {
221: Expr e = (Expr) left;
222: if (e.getOperator() == ARRAY) {
223: atArrayAssign(expr, op, (Expr) left, right);
224: return;
225: }
226: }
227:
228: atFieldAssign(expr, op, left, right);
229: }
230: }
231:
232: /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
233: *
234: * expr and var can be null.
235: */
236: private void atVariableAssign(Expr expr, int op, Variable var,
237: Declarator d, ASTree right) throws CompileError {
238: int varType = d.getType();
239: int varArray = d.getArrayDim();
240: String varClass = d.getClassName();
241:
242: if (op != '=')
243: atVariable(var);
244:
245: right.accept(this );
246: exprType = varType;
247: arrayDim = varArray;
248: className = varClass;
249: }
250:
251: private void atArrayAssign(Expr expr, int op, Expr array,
252: ASTree right) throws CompileError {
253: atArrayRead(array.oprand1(), array.oprand2());
254: int aType = exprType;
255: int aDim = arrayDim;
256: String cname = className;
257: right.accept(this );
258: exprType = aType;
259: arrayDim = aDim;
260: className = cname;
261: }
262:
263: protected void atFieldAssign(Expr expr, int op, ASTree left,
264: ASTree right) throws CompileError {
265: CtField f = fieldAccess(left);
266: atFieldRead(f);
267: int fType = exprType;
268: int fDim = arrayDim;
269: String cname = className;
270: right.accept(this );
271: exprType = fType;
272: arrayDim = fDim;
273: className = cname;
274: }
275:
276: public void atCondExpr(CondExpr expr) throws CompileError {
277: booleanExpr(expr.condExpr());
278: expr.thenExpr().accept(this );
279: int type1 = exprType;
280: int dim1 = arrayDim;
281: String cname1 = className;
282: expr.elseExpr().accept(this );
283:
284: if (dim1 == 0 && dim1 == arrayDim)
285: if (CodeGen.rightIsStrong(type1, exprType))
286: expr
287: .setThen(new CastExpr(exprType, 0, expr
288: .thenExpr()));
289: else if (CodeGen.rightIsStrong(exprType, type1)) {
290: expr.setElse(new CastExpr(type1, 0, expr.elseExpr()));
291: exprType = type1;
292: }
293: }
294:
295: /*
296: * If atBinExpr() substitutes a new expression for the original
297: * binary-operator expression, it changes the operator name to '+'
298: * (if the original is not '+') and sets the new expression to the
299: * left-hand-side expression and null to the right-hand-side expression.
300: */
301: public void atBinExpr(BinExpr expr) throws CompileError {
302: int token = expr.getOperator();
303: int k = CodeGen.lookupBinOp(token);
304: if (k >= 0) {
305: /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
306: */
307: if (token == '+') {
308: Expr e = atPlusExpr(expr);
309: if (e != null) {
310: /* String concatenation has been translated into
311: * an expression using StringBuffer.
312: */
313: e = CallExpr.makeCall(Expr.make('.', e, new Member(
314: "toString")), null);
315: expr.setOprand1(e);
316: expr.setOprand2(null); // <---- look at this!
317: className = jvmJavaLangString;
318: }
319: } else {
320: ASTree left = expr.oprand1();
321: ASTree right = expr.oprand2();
322: left.accept(this );
323: int type1 = exprType;
324: right.accept(this );
325: if (!isConstant(expr, token, left, right))
326: computeBinExprType(expr, token, type1);
327: }
328: } else {
329: /* equation: &&, ||, ==, !=, <=, >=, <, >
330: */
331: booleanExpr(expr);
332: }
333: }
334:
335: /* EXPR must be a + expression.
336: * atPlusExpr() returns non-null if the given expression is string
337: * concatenation. The returned value is "new StringBuffer().append..".
338: */
339: private Expr atPlusExpr(BinExpr expr) throws CompileError {
340: ASTree left = expr.oprand1();
341: ASTree right = expr.oprand2();
342: if (right == null) {
343: // this expression has been already type-checked.
344: // see atBinExpr() above.
345: left.accept(this );
346: return null;
347: }
348:
349: if (isPlusExpr(left)) {
350: Expr newExpr = atPlusExpr((BinExpr) left);
351: if (newExpr != null) {
352: right.accept(this );
353: exprType = CLASS;
354: arrayDim = 0;
355: className = "java/lang/StringBuffer";
356: return makeAppendCall(newExpr, right);
357: }
358: } else
359: left.accept(this );
360:
361: int type1 = exprType;
362: int dim1 = arrayDim;
363: String cname = className;
364: right.accept(this );
365:
366: if (isConstant(expr, '+', left, right))
367: return null;
368:
369: if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString
370: .equals(cname))
371: || (exprType == CLASS && arrayDim == 0 && jvmJavaLangString
372: .equals(className))) {
373: ASTList sbufClass = ASTList.make(new Symbol("java"),
374: new Symbol("lang"), new Symbol("StringBuffer"));
375: ASTree e = new NewExpr(sbufClass, null);
376: exprType = CLASS;
377: arrayDim = 0;
378: className = "java/lang/StringBuffer";
379: return makeAppendCall(makeAppendCall(e, left), right);
380: } else {
381: computeBinExprType(expr, '+', type1);
382: return null;
383: }
384: }
385:
386: private boolean isConstant(BinExpr expr, int op, ASTree left,
387: ASTree right) throws CompileError {
388: left = stripPlusExpr(left);
389: right = stripPlusExpr(right);
390: ASTree newExpr = null;
391: if (left instanceof StringL && right instanceof StringL
392: && op == '+')
393: newExpr = new StringL(((StringL) left).get()
394: + ((StringL) right).get());
395: else if (left instanceof IntConst)
396: newExpr = ((IntConst) left).compute(op, right);
397: else if (left instanceof DoubleConst)
398: newExpr = ((DoubleConst) left).compute(op, right);
399:
400: if (newExpr == null)
401: return false; // not a constant expression
402: else {
403: expr.setOperator('+');
404: expr.setOprand1(newExpr);
405: expr.setOprand2(null);
406: newExpr.accept(this ); // for setting exprType, arrayDim, ...
407: return true;
408: }
409: }
410:
411: /* CodeGen.atSwitchStmnt() also calls stripPlusExpr().
412: */
413: static ASTree stripPlusExpr(ASTree expr) {
414: if (expr instanceof BinExpr) {
415: BinExpr e = (BinExpr) expr;
416: if (e.getOperator() == '+' && e.oprand2() == null)
417: return e.getLeft();
418: } else if (expr instanceof Expr) { // note: BinExpr extends Expr.
419: Expr e = (Expr) expr;
420: int op = e.getOperator();
421: if (op == MEMBER) {
422: ASTree cexpr = getConstantFieldValue((Member) e
423: .oprand2());
424: if (cexpr != null)
425: return cexpr;
426: } else if (op == '+' && e.getRight() == null)
427: return e.getLeft();
428: } else if (expr instanceof Member) {
429: ASTree cexpr = getConstantFieldValue((Member) expr);
430: if (cexpr != null)
431: return cexpr;
432: }
433:
434: return expr;
435: }
436:
437: /**
438: * If MEM is a static final field, this method returns a constant
439: * expression representing the value of that field.
440: */
441: private static ASTree getConstantFieldValue(Member mem) {
442: return getConstantFieldValue(mem.getField());
443: }
444:
445: public static ASTree getConstantFieldValue(CtField f) {
446: if (f == null)
447: return null;
448:
449: Object value = f.getConstantValue();
450: if (value == null)
451: return null;
452:
453: if (value instanceof String)
454: return new StringL((String) value);
455: else if (value instanceof Double || value instanceof Float) {
456: int token = (value instanceof Double) ? DoubleConstant
457: : FloatConstant;
458: return new DoubleConst(((Number) value).doubleValue(),
459: token);
460: } else if (value instanceof Number) {
461: int token = (value instanceof Long) ? LongConstant
462: : IntConstant;
463: return new IntConst(((Number) value).longValue(), token);
464: } else if (value instanceof Boolean)
465: return new Keyword(
466: ((Boolean) value).booleanValue() ? TokenId.TRUE
467: : TokenId.FALSE);
468: else
469: return null;
470: }
471:
472: private static boolean isPlusExpr(ASTree expr) {
473: if (expr instanceof BinExpr) {
474: BinExpr bexpr = (BinExpr) expr;
475: int token = bexpr.getOperator();
476: return token == '+';
477: }
478:
479: return false;
480: }
481:
482: private static Expr makeAppendCall(ASTree target, ASTree arg) {
483: return CallExpr.makeCall(Expr.make('.', target, new Member(
484: "append")), new ASTList(arg));
485: }
486:
487: private void computeBinExprType(BinExpr expr, int token, int type1)
488: throws CompileError {
489: // arrayDim should be 0.
490: int type2 = exprType;
491: if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
492: exprType = type1;
493: else
494: insertCast(expr, type1, type2);
495:
496: if (CodeGen.isP_INT(exprType))
497: exprType = INT; // type1 may be BYTE, ...
498: }
499:
500: private void booleanExpr(ASTree expr) throws CompileError {
501: int op = CodeGen.getCompOperator(expr);
502: if (op == EQ) { // ==, !=, ...
503: BinExpr bexpr = (BinExpr) expr;
504: bexpr.oprand1().accept(this );
505: int type1 = exprType;
506: int dim1 = arrayDim;
507: bexpr.oprand2().accept(this );
508: if (dim1 == 0 && arrayDim == 0)
509: insertCast(bexpr, type1, exprType);
510: } else if (op == '!')
511: ((Expr) expr).oprand1().accept(this );
512: else if (op == ANDAND || op == OROR) {
513: BinExpr bexpr = (BinExpr) expr;
514: bexpr.oprand1().accept(this );
515: bexpr.oprand2().accept(this );
516: } else
517: // others
518: expr.accept(this );
519:
520: exprType = BOOLEAN;
521: arrayDim = 0;
522: }
523:
524: private void insertCast(BinExpr expr, int type1, int type2)
525: throws CompileError {
526: if (CodeGen.rightIsStrong(type1, type2))
527: expr.setLeft(new CastExpr(type2, 0, expr.oprand1()));
528: else
529: exprType = type1;
530: }
531:
532: public void atCastExpr(CastExpr expr) throws CompileError {
533: String cname = resolveClassName(expr.getClassName());
534: expr.getOprand().accept(this );
535: exprType = expr.getType();
536: arrayDim = expr.getArrayDim();
537: className = cname;
538: }
539:
540: public void atInstanceOfExpr(InstanceOfExpr expr)
541: throws CompileError {
542: expr.getOprand().accept(this );
543: exprType = BOOLEAN;
544: arrayDim = 0;
545: }
546:
547: public void atExpr(Expr expr) throws CompileError {
548: // array access, member access,
549: // (unary) +, (unary) -, ++, --, !, ~
550:
551: int token = expr.getOperator();
552: ASTree oprand = expr.oprand1();
553: if (token == '.') {
554: String member = ((Symbol) expr.oprand2()).get();
555: if (member.equals("length"))
556: atArrayLength(expr);
557: else if (member.equals("class"))
558: atClassObject(expr); // .class
559: else
560: atFieldRead(expr);
561: } else if (token == MEMBER) { // field read
562: String member = ((Symbol) expr.oprand2()).get();
563: if (member.equals("class"))
564: atClassObject(expr); // .class
565: else
566: atFieldRead(expr);
567: } else if (token == ARRAY)
568: atArrayRead(oprand, expr.oprand2());
569: else if (token == PLUSPLUS || token == MINUSMINUS)
570: atPlusPlus(token, oprand, expr);
571: else if (token == '!')
572: booleanExpr(expr);
573: else if (token == CALL) // method call
574: fatal();
575: else {
576: oprand.accept(this );
577: if (!isConstant(expr, token, oprand))
578: if (token == '-' || token == '~')
579: if (CodeGen.isP_INT(exprType))
580: exprType = INT; // type may be BYTE, ...
581: }
582: }
583:
584: private boolean isConstant(Expr expr, int op, ASTree oprand) {
585: oprand = stripPlusExpr(oprand);
586: if (oprand instanceof IntConst) {
587: IntConst c = (IntConst) oprand;
588: long v = c.get();
589: if (op == '-')
590: v = -v;
591: else if (op == '~')
592: v = ~v;
593: else
594: return false;
595:
596: c.set(v);
597: } else if (oprand instanceof DoubleConst) {
598: DoubleConst c = (DoubleConst) oprand;
599: if (op == '-')
600: c.set(-c.get());
601: else
602: return false;
603: } else
604: return false;
605:
606: expr.setOperator('+');
607: return true;
608: }
609:
610: public void atCallExpr(CallExpr expr) throws CompileError {
611: String mname = null;
612: CtClass targetClass = null;
613: ASTree method = expr.oprand1();
614: ASTList args = (ASTList) expr.oprand2();
615:
616: if (method instanceof Member) {
617: mname = ((Member) method).get();
618: targetClass = this Class;
619: } else if (method instanceof Keyword) { // constructor
620: mname = MethodInfo.nameInit; // <init>
621: if (((Keyword) method).get() == SUPER)
622: targetClass = MemberResolver.getSuperclass(this Class);
623: else
624: targetClass = this Class;
625: } else if (method instanceof Expr) {
626: Expr e = (Expr) method;
627: mname = ((Symbol) e.oprand2()).get();
628: int op = e.getOperator();
629: if (op == MEMBER) // static method
630: targetClass = resolver.lookupClass(((Symbol) e
631: .oprand1()).get(), false);
632: else if (op == '.') {
633: ASTree target = e.oprand1();
634: try {
635: target.accept(this );
636: } catch (NoFieldException nfe) {
637: if (nfe.getExpr() != target)
638: throw nfe;
639:
640: // it should be a static method.
641: exprType = CLASS;
642: arrayDim = 0;
643: className = nfe.getField(); // JVM-internal
644: e.setOperator(MEMBER);
645: e.setOprand1(new Symbol(MemberResolver
646: .jvmToJavaName(className)));
647: }
648:
649: if (arrayDim > 0)
650: targetClass = resolver.lookupClass(javaLangObject,
651: true);
652: else if (exprType == CLASS /* && arrayDim == 0 */)
653: targetClass = resolver
654: .lookupClassByJvmName(className);
655: else
656: badMethod();
657: } else
658: badMethod();
659: } else
660: fatal();
661:
662: MemberResolver.Method minfo = atMethodCallCore(targetClass,
663: mname, args);
664: expr.setMethod(minfo);
665: }
666:
667: private static void badMethod() throws CompileError {
668: throw new CompileError("bad method");
669: }
670:
671: /**
672: * @return a pair of the class declaring the invoked method
673: * and the MethodInfo of that method. Never null.
674: */
675: public MemberResolver.Method atMethodCallCore(CtClass targetClass,
676: String mname, ASTList args) throws CompileError {
677: int nargs = getMethodArgsLength(args);
678: int[] types = new int[nargs];
679: int[] dims = new int[nargs];
680: String[] cnames = new String[nargs];
681: atMethodArgs(args, types, dims, cnames);
682:
683: MemberResolver.Method found = resolver.lookupMethod(
684: targetClass, this Class, this Method, mname, types, dims,
685: cnames);
686: if (found == null) {
687: String clazz = targetClass.getName();
688: String signature = argTypesToString(types, dims, cnames);
689: String msg;
690: if (mname.equals(MethodInfo.nameInit))
691: msg = "cannot find constructor " + clazz + signature;
692: else
693: msg = mname + signature + " not found in " + clazz;
694:
695: throw new CompileError(msg);
696: }
697:
698: String desc = found.info.getDescriptor();
699: setReturnType(desc);
700: return found;
701: }
702:
703: public int getMethodArgsLength(ASTList args) {
704: return ASTList.length(args);
705: }
706:
707: public void atMethodArgs(ASTList args, int[] types, int[] dims,
708: String[] cnames) throws CompileError {
709: int i = 0;
710: while (args != null) {
711: ASTree a = args.head();
712: a.accept(this );
713: types[i] = exprType;
714: dims[i] = arrayDim;
715: cnames[i] = className;
716: ++i;
717: args = args.tail();
718: }
719: }
720:
721: void setReturnType(String desc) throws CompileError {
722: int i = desc.indexOf(')');
723: if (i < 0)
724: badMethod();
725:
726: char c = desc.charAt(++i);
727: int dim = 0;
728: while (c == '[') {
729: ++dim;
730: c = desc.charAt(++i);
731: }
732:
733: arrayDim = dim;
734: if (c == 'L') {
735: int j = desc.indexOf(';', i + 1);
736: if (j < 0)
737: badMethod();
738:
739: exprType = CLASS;
740: className = desc.substring(i + 1, j);
741: } else {
742: exprType = MemberResolver.descToType(c);
743: className = null;
744: }
745: }
746:
747: private void atFieldRead(ASTree expr) throws CompileError {
748: atFieldRead(fieldAccess(expr));
749: }
750:
751: private void atFieldRead(CtField f) throws CompileError {
752: FieldInfo finfo = f.getFieldInfo2();
753: String type = finfo.getDescriptor();
754:
755: int i = 0;
756: int dim = 0;
757: char c = type.charAt(i);
758: while (c == '[') {
759: ++dim;
760: c = type.charAt(++i);
761: }
762:
763: arrayDim = dim;
764: exprType = MemberResolver.descToType(c);
765:
766: if (c == 'L')
767: className = type.substring(i + 1, type.indexOf(';', i + 1));
768: else
769: className = null;
770: }
771:
772: /* if EXPR is to access a static field, fieldAccess() translates EXPR
773: * into an expression using '#' (MEMBER). For example, it translates
774: * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation
775: * speeds up type resolution by MemberCodeGen.
776: */
777: protected CtField fieldAccess(ASTree expr) throws CompileError {
778: if (expr instanceof Member) {
779: Member mem = (Member) expr;
780: String name = mem.get();
781: try {
782: CtField f = this Class.getField(name);
783: if (Modifier.isStatic(f.getModifiers()))
784: mem.setField(f);
785:
786: return f;
787: } catch (NotFoundException e) {
788: // EXPR might be part of a static member access?
789: throw new NoFieldException(name, expr);
790: }
791: } else if (expr instanceof Expr) {
792: Expr e = (Expr) expr;
793: int op = e.getOperator();
794: if (op == MEMBER) {
795: Member mem = (Member) e.oprand2();
796: CtField f = resolver.lookupField(((Symbol) e.oprand1())
797: .get(), mem);
798: mem.setField(f);
799: return f;
800: } else if (op == '.')
801: try {
802: e.oprand1().accept(this );
803: if (exprType == CLASS && arrayDim == 0)
804: return resolver.lookupFieldByJvmName(className,
805: (Symbol) e.oprand2());
806: } catch (NoFieldException nfe) {
807: if (nfe.getExpr() != e.oprand1())
808: throw nfe;
809:
810: /* EXPR should be a static field.
811: * If EXPR might be part of a qualified class name,
812: * lookupFieldByJvmName2() throws NoFieldException.
813: */
814: Member fname = (Member) e.oprand2();
815: String jvmClassName = nfe.getField();
816: CtField f = resolver.lookupFieldByJvmName2(
817: jvmClassName, fname, expr);
818: e.setOperator(MEMBER);
819: e.setOprand1(new Symbol(MemberResolver
820: .jvmToJavaName(jvmClassName)));
821: fname.setField(f);
822: return f;
823: }
824: }
825:
826: throw new CompileError("bad filed access");
827: }
828:
829: public void atClassObject(Expr expr) throws CompileError {
830: exprType = CLASS;
831: arrayDim = 0;
832: className = jvmJavaLangClass;
833: }
834:
835: public void atArrayLength(Expr expr) throws CompileError {
836: expr.oprand1().accept(this );
837: exprType = INT;
838: arrayDim = 0;
839: }
840:
841: public void atArrayRead(ASTree array, ASTree index)
842: throws CompileError {
843: array.accept(this );
844: int type = exprType;
845: int dim = arrayDim;
846: String cname = className;
847: index.accept(this );
848: exprType = type;
849: arrayDim = dim - 1;
850: className = cname;
851: }
852:
853: private void atPlusPlus(int token, ASTree oprand, Expr expr)
854: throws CompileError {
855: boolean isPost = oprand == null; // ++i or i++?
856: if (isPost)
857: oprand = expr.oprand2();
858:
859: if (oprand instanceof Variable) {
860: Declarator d = ((Variable) oprand).getDeclarator();
861: exprType = d.getType();
862: arrayDim = d.getArrayDim();
863: } else {
864: if (oprand instanceof Expr) {
865: Expr e = (Expr) oprand;
866: if (e.getOperator() == ARRAY) {
867: atArrayRead(e.oprand1(), e.oprand2());
868: // arrayDim should be 0.
869: int t = exprType;
870: if (t == INT || t == BYTE || t == CHAR
871: || t == SHORT)
872: exprType = INT;
873:
874: return;
875: }
876: }
877:
878: atFieldPlusPlus(oprand);
879: }
880: }
881:
882: protected void atFieldPlusPlus(ASTree oprand) throws CompileError {
883: CtField f = fieldAccess(oprand);
884: atFieldRead(f);
885: int t = exprType;
886: if (t == INT || t == BYTE || t == CHAR || t == SHORT)
887: exprType = INT;
888: }
889:
890: public void atMember(Member mem) throws CompileError {
891: atFieldRead(mem);
892: }
893:
894: public void atVariable(Variable v) throws CompileError {
895: Declarator d = v.getDeclarator();
896: exprType = d.getType();
897: arrayDim = d.getArrayDim();
898: className = d.getClassName();
899: }
900:
901: public void atKeyword(Keyword k) throws CompileError {
902: arrayDim = 0;
903: int token = k.get();
904: switch (token) {
905: case TRUE:
906: case FALSE:
907: exprType = BOOLEAN;
908: break;
909: case NULL:
910: exprType = NULL;
911: break;
912: case THIS:
913: case SUPER:
914: exprType = CLASS;
915: if (token == THIS)
916: className = getThisName();
917: else
918: className = getSuperName();
919: break;
920: default:
921: fatal();
922: }
923: }
924:
925: public void atStringL(StringL s) throws CompileError {
926: exprType = CLASS;
927: arrayDim = 0;
928: className = jvmJavaLangString;
929: }
930:
931: public void atIntConst(IntConst i) throws CompileError {
932: arrayDim = 0;
933: int type = i.getType();
934: if (type == IntConstant || type == CharConstant)
935: exprType = (type == IntConstant ? INT : CHAR);
936: else
937: exprType = LONG;
938: }
939:
940: public void atDoubleConst(DoubleConst d) throws CompileError {
941: arrayDim = 0;
942: if (d.getType() == DoubleConstant)
943: exprType = DOUBLE;
944: else
945: exprType = FLOAT;
946: }
947: }
|