1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.compiler;
17 
18 import javassist.*;
19 import javassist.bytecode.*;
20 import javassist.compiler.ast.*;
21 
22 import java.util.ArrayList;
23 
24 /* Code generator methods depending on javassist.* classes.
25  */
26 public class MemberCodeGen extends CodeGen {
27     protected MemberResolver resolver;
28     protected CtClass   thisClass;
29     protected MethodInfo thisMethod;
30 
31     protected boolean resultStatic;
32 
MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp)33     public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
34         super(b);
35         resolver = new MemberResolver(cp);
36         thisClass = cc;
37         thisMethod = null;
38     }
39 
40     /**
41      * Returns the major version of the class file
42      * targeted by this compilation.
43      */
getMajorVersion()44     public int getMajorVersion() {
45         ClassFile cf = thisClass.getClassFile2();
46         if (cf == null)
47             return ClassFile.MAJOR_VERSION;     // JDK 1.3
48         else
49             return cf.getMajorVersion();
50     }
51 
52     /**
53      * Records the currently compiled method.
54      */
setThisMethod(CtMethod m)55     public void setThisMethod(CtMethod m) {
56         thisMethod = m.getMethodInfo2();
57         if (typeChecker != null)
58             typeChecker.setThisMethod(thisMethod);
59     }
60 
getThisClass()61     public CtClass getThisClass() { return thisClass; }
62 
63     /**
64      * Returns the JVM-internal representation of this class name.
65      */
getThisName()66     protected String getThisName() {
67         return MemberResolver.javaToJvmName(thisClass.getName());
68     }
69 
70     /**
71      * Returns the JVM-internal representation of this super class name.
72      */
getSuperName()73     protected String getSuperName() throws CompileError {
74         return MemberResolver.javaToJvmName(
75                         MemberResolver.getSuperclass(thisClass).getName());
76     }
77 
insertDefaultSuperCall()78     protected void insertDefaultSuperCall() throws CompileError {
79         bytecode.addAload(0);
80         bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
81                                   "<init>", "()V");
82     }
83 
84     static class JsrHook extends ReturnHook {
85         ArrayList jsrList;
86         CodeGen cgen;
87         int var;
88 
JsrHook(CodeGen gen)89         JsrHook(CodeGen gen) {
90             super(gen);
91             jsrList = new ArrayList();
92             cgen = gen;
93             var = -1;
94         }
95 
getVar(int size)96         private int getVar(int size) {
97             if (var < 0) {
98                 var = cgen.getMaxLocals();
99                 cgen.incMaxLocals(size);
100             }
101 
102             return var;
103         }
104 
jsrJmp(Bytecode b)105         private void jsrJmp(Bytecode b) {
106             b.addOpcode(Opcode.GOTO);
107             jsrList.add(new int[] {b.currentPc(), var});
108             b.addIndex(0);
109         }
110 
doit(Bytecode b, int opcode)111         protected boolean doit(Bytecode b, int opcode) {
112             switch (opcode) {
113             case Opcode.RETURN :
114                 jsrJmp(b);
115                 break;
116             case ARETURN :
117                 b.addAstore(getVar(1));
118                 jsrJmp(b);
119                 b.addAload(var);
120                 break;
121             case IRETURN :
122                 b.addIstore(getVar(1));
123                 jsrJmp(b);
124                 b.addIload(var);
125                 break;
126             case LRETURN :
127                 b.addLstore(getVar(2));
128                 jsrJmp(b);
129                 b.addLload(var);
130                 break;
131             case DRETURN :
132                 b.addDstore(getVar(2));
133                 jsrJmp(b);
134                 b.addDload(var);
135                 break;
136             case FRETURN :
137                 b.addFstore(getVar(1));
138                 jsrJmp(b);
139                 b.addFload(var);
140                 break;
141             default :
142                 throw new RuntimeException("fatal");
143             }
144 
145             return false;
146         }
147     }
148 
149     static class JsrHook2 extends ReturnHook {
150         int var;
151         int target;
152 
JsrHook2(CodeGen gen, int[] retTarget)153         JsrHook2(CodeGen gen, int[] retTarget) {
154             super(gen);
155             target = retTarget[0];
156             var = retTarget[1];
157         }
158 
doit(Bytecode b, int opcode)159         protected boolean doit(Bytecode b, int opcode) {
160             switch (opcode) {
161             case Opcode.RETURN :
162                 break;
163             case ARETURN :
164                 b.addAstore(var);
165                 break;
166             case IRETURN :
167                 b.addIstore(var);
168                 break;
169             case LRETURN :
170                 b.addLstore(var);
171                 break;
172             case DRETURN :
173                 b.addDstore(var);
174                 break;
175             case FRETURN :
176                 b.addFstore(var);
177                 break;
178             default :
179                 throw new RuntimeException("fatal");
180             }
181 
182             b.addOpcode(Opcode.GOTO);
183             b.addIndex(target - b.currentPc() + 3);
184             return true;
185         }
186     }
187 
atTryStmnt(Stmnt st)188     protected void atTryStmnt(Stmnt st) throws CompileError {
189         Bytecode bc = bytecode;
190         Stmnt body = (Stmnt)st.getLeft();
191         if (body == null)
192             return;
193 
194         ASTList catchList = (ASTList)st.getRight().getLeft();
195         Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
196         ArrayList gotoList = new ArrayList();
197 
198         JsrHook jsrHook = null;
199         if (finallyBlock != null)
200             jsrHook = new JsrHook(this);
201 
202         int start = bc.currentPc();
203         body.accept(this);
204         int end = bc.currentPc();
205         if (start == end)
206             throw new CompileError("empty try block");
207 
208         boolean tryNotReturn = !hasReturned;
209         if (tryNotReturn) {
210             bc.addOpcode(Opcode.GOTO);
211             gotoList.add(new Integer(bc.currentPc()));
212             bc.addIndex(0);   // correct later
213         }
214 
215         int var = getMaxLocals();
216         incMaxLocals(1);
217         while (catchList != null) {
218             // catch clause
219             Pair p = (Pair)catchList.head();
220             catchList = catchList.tail();
221             Declarator decl = (Declarator)p.getLeft();
222             Stmnt block = (Stmnt)p.getRight();
223 
224             decl.setLocalVar(var);
225 
226             CtClass type = resolver.lookupClassByJvmName(decl.getClassName());
227             decl.setClassName(MemberResolver.javaToJvmName(type.getName()));
228             bc.addExceptionHandler(start, end, bc.currentPc(), type);
229             bc.growStack(1);
230             bc.addAstore(var);
231             hasReturned = false;
232             if (block != null)
233                 block.accept(this);
234 
235             if (!hasReturned) {
236                 bc.addOpcode(Opcode.GOTO);
237                 gotoList.add(new Integer(bc.currentPc()));
238                 bc.addIndex(0);   // correct later
239                 tryNotReturn = true;
240             }
241         }
242 
243         if (finallyBlock != null) {
244             jsrHook.remove(this);
245             // catch (any) clause
246             int pcAnyCatch = bc.currentPc();
247             bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
248             bc.growStack(1);
249             bc.addAstore(var);
250             hasReturned = false;
251             finallyBlock.accept(this);
252             if (!hasReturned) {
253                 bc.addAload(var);
254                 bc.addOpcode(ATHROW);
255             }
256 
257             addFinally(jsrHook.jsrList, finallyBlock);
258         }
259 
260         int pcEnd = bc.currentPc();
261         patchGoto(gotoList, pcEnd);
262         hasReturned = !tryNotReturn;
263         if (finallyBlock != null) {
264             if (tryNotReturn)
265                 finallyBlock.accept(this);
266         }
267     }
268 
269     /**
270      * Adds a finally clause for earch return statement.
271      */
addFinally(ArrayList returnList, Stmnt finallyBlock)272     private void addFinally(ArrayList returnList, Stmnt finallyBlock)
273         throws CompileError
274     {
275         Bytecode bc = bytecode;
276         int n = returnList.size();
277         for (int i = 0; i < n; ++i) {
278             final int[] ret = (int[])returnList.get(i);
279             int pc = ret[0];
280             bc.write16bit(pc, bc.currentPc() - pc + 1);
281             ReturnHook hook = new JsrHook2(this, ret);
282             finallyBlock.accept(this);
283             hook.remove(this);
284             if (!hasReturned) {
285                 bc.addOpcode(Opcode.GOTO);
286                 bc.addIndex(pc + 3 - bc.currentPc());
287             }
288         }
289     }
290 
atNewExpr(NewExpr expr)291     public void atNewExpr(NewExpr expr) throws CompileError {
292         if (expr.isArray())
293             atNewArrayExpr(expr);
294         else {
295             CtClass clazz = resolver.lookupClassByName(expr.getClassName());
296             String cname = clazz.getName();
297             ASTList args = expr.getArguments();
298             bytecode.addNew(cname);
299             bytecode.addOpcode(DUP);
300 
301             atMethodCallCore(clazz, MethodInfo.nameInit, args,
302                              false, true, -1, null);
303 
304             exprType = CLASS;
305             arrayDim = 0;
306             className = MemberResolver.javaToJvmName(cname);
307         }
308     }
309 
atNewArrayExpr(NewExpr expr)310     public void atNewArrayExpr(NewExpr expr) throws CompileError {
311         int type = expr.getArrayType();
312         ASTList size = expr.getArraySize();
313         ASTList classname = expr.getClassName();
314         ArrayInit init = expr.getInitializer();
315         if (size.length() > 1) {
316             if (init != null)
317                 throw new CompileError(
318                         "sorry, multi-dimensional array initializer " +
319                         "for new is not supported");
320 
321             atMultiNewArray(type, classname, size);
322             return;
323         }
324 
325         ASTree sizeExpr = size.head();
326         atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init);
327     }
328 
atNewArrayExpr2(int type, ASTree sizeExpr, String jvmClassname, ArrayInit init)329     private void atNewArrayExpr2(int type, ASTree sizeExpr,
330                         String jvmClassname, ArrayInit init) throws CompileError {
331         if (init == null)
332             if (sizeExpr == null)
333                 throw new CompileError("no array size");
334             else
335                 sizeExpr.accept(this);
336         else
337             if (sizeExpr == null) {
338                 int s = init.length();
339                 bytecode.addIconst(s);
340             }
341             else
342                 throw new CompileError("unnecessary array size specified for new");
343 
344         String elementClass;
345         if (type == CLASS) {
346             elementClass = resolveClassName(jvmClassname);
347             bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass));
348         }
349         else {
350             elementClass = null;
351             int atype = 0;
352             switch (type) {
353             case BOOLEAN :
354                 atype = T_BOOLEAN;
355                 break;
356             case CHAR :
357                 atype = T_CHAR;
358                 break;
359             case FLOAT :
360                 atype = T_FLOAT;
361                 break;
362             case DOUBLE :
363                 atype = T_DOUBLE;
364                 break;
365             case BYTE :
366                 atype = T_BYTE;
367                 break;
368             case SHORT :
369                 atype = T_SHORT;
370                 break;
371             case INT :
372                 atype = T_INT;
373                 break;
374             case LONG :
375                 atype = T_LONG;
376                 break;
377             default :
378                 badNewExpr();
379                 break;
380             }
381 
382             bytecode.addOpcode(NEWARRAY);
383             bytecode.add(atype);
384         }
385 
386         if (init != null) {
387             int s = init.length();
388             ASTList list = init;
389             for (int i = 0; i < s; i++) {
390                 bytecode.addOpcode(DUP);
391                 bytecode.addIconst(i);
392                 list.head().accept(this);
393                 if (!isRefType(type))
394                     atNumCastExpr(exprType, type);
395 
396                 bytecode.addOpcode(getArrayWriteOp(type, 0));
397                 list = list.tail();
398             }
399         }
400 
401         exprType = type;
402         arrayDim = 1;
403         className = elementClass;
404     }
405 
badNewExpr()406     private static void badNewExpr() throws CompileError {
407         throw new CompileError("bad new expression");
408     }
409 
atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass)410     protected void atArrayVariableAssign(ArrayInit init, int varType,
411                                          int varArray, String varClass) throws CompileError {
412         atNewArrayExpr2(varType, null, varClass, init);
413     }
414 
atArrayInit(ArrayInit init)415     public void atArrayInit(ArrayInit init) throws CompileError {
416         throw new CompileError("array initializer is not supported");
417     }
418 
atMultiNewArray(int type, ASTList classname, ASTList size)419     protected void atMultiNewArray(int type, ASTList classname, ASTList size)
420         throws CompileError
421     {
422         int count, dim;
423         dim = size.length();
424         for (count = 0; size != null; size = size.tail()) {
425             ASTree s = size.head();
426             if (s == null)
427                 break;          // int[][][] a = new int[3][4][];
428 
429             ++count;
430             s.accept(this);
431             if (exprType != INT)
432                 throw new CompileError("bad type for array size");
433         }
434 
435         String desc;
436         exprType = type;
437         arrayDim = dim;
438         if (type == CLASS) {
439             className = resolveClassName(classname);
440             desc = toJvmArrayName(className, dim);
441         }
442         else
443             desc = toJvmTypeName(type, dim);
444 
445         bytecode.addMultiNewarray(desc, count);
446     }
447 
atCallExpr(CallExpr expr)448     public void atCallExpr(CallExpr expr) throws CompileError {
449         String mname = null;
450         CtClass targetClass = null;
451         ASTree method = expr.oprand1();
452         ASTList args = (ASTList)expr.oprand2();
453         boolean isStatic = false;
454         boolean isSpecial = false;
455         int aload0pos = -1;
456 
457         MemberResolver.Method cached = expr.getMethod();
458         if (method instanceof Member) {
459             mname = ((Member)method).get();
460             targetClass = thisClass;
461             if (inStaticMethod || (cached != null && cached.isStatic()))
462                 isStatic = true;            // should be static
463             else {
464                 aload0pos = bytecode.currentPc();
465                 bytecode.addAload(0);       // this
466             }
467         }
468         else if (method instanceof Keyword) {   // constructor
469             isSpecial = true;
470             mname = MethodInfo.nameInit;        // <init>
471             targetClass = thisClass;
472             if (inStaticMethod)
473                 throw new CompileError("a constructor cannot be static");
474             else
475                 bytecode.addAload(0);   // this
476 
477             if (((Keyword)method).get() == SUPER)
478                 targetClass = MemberResolver.getSuperclass(targetClass);
479         }
480         else if (method instanceof Expr) {
481             Expr e = (Expr)method;
482             mname = ((Symbol)e.oprand2()).get();
483             int op = e.getOperator();
484             if (op == MEMBER) {                 // static method
485                 targetClass
486                     = resolver.lookupClass(((Symbol)e.oprand1()).get(), false);
487                 isStatic = true;
488             }
489             else if (op == '.') {
490                 ASTree target = e.oprand1();
491                 if (target instanceof Keyword)
492                     if (((Keyword)target).get() == SUPER)
493                         isSpecial = true;
494 
495                 try {
496                     target.accept(this);
497                 }
498                 catch (NoFieldException nfe) {
499                     if (nfe.getExpr() != target)
500                         throw nfe;
501 
502                     // it should be a static method.
503                     exprType = CLASS;
504                     arrayDim = 0;
505                     className = nfe.getField(); // JVM-internal
506                     resolver.recordPackage(className);
507                     isStatic = true;
508                 }
509 
510                 if (arrayDim > 0)
511                     targetClass = resolver.lookupClass(javaLangObject, true);
512                 else if (exprType == CLASS /* && arrayDim == 0 */)
513                     targetClass = resolver.lookupClassByJvmName(className);
514                 else
515                     badMethod();
516             }
517             else
518                 badMethod();
519         }
520         else
521             fatal();
522 
523         atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
524                          aload0pos, cached);
525     }
526 
badMethod()527     private static void badMethod() throws CompileError {
528         throw new CompileError("bad method");
529     }
530 
531     /*
532      * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
533      *
534      * @param targetClass       the class at which method lookup starts.
535      * @param found         not null if the method look has been already done.
536      */
atMethodCallCore(CtClass targetClass, String mname, ASTList args, boolean isStatic, boolean isSpecial, int aload0pos, MemberResolver.Method found)537     public void atMethodCallCore(CtClass targetClass, String mname,
538                         ASTList args, boolean isStatic, boolean isSpecial,
539                         int aload0pos, MemberResolver.Method found)
540         throws CompileError
541     {
542         int nargs = getMethodArgsLength(args);
543         int[] types = new int[nargs];
544         int[] dims = new int[nargs];
545         String[] cnames = new String[nargs];
546 
547         if (!isStatic && found != null && found.isStatic()) {
548             bytecode.addOpcode(POP);
549             isStatic = true;
550         }
551 
552         int stack = bytecode.getStackDepth();
553 
554         // generate code for evaluating arguments.
555         atMethodArgs(args, types, dims, cnames);
556 
557         // used by invokeinterface
558         int count = bytecode.getStackDepth() - stack + 1;
559 
560         if (found == null)
561             found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
562                                           mname, types, dims, cnames);
563 
564         if (found == null) {
565             String msg;
566             if (mname.equals(MethodInfo.nameInit))
567                 msg = "constructor not found";
568             else
569                 msg = "Method " + mname + " not found in "
570                     + targetClass.getName();
571 
572             throw new CompileError(msg);
573         }
574 
575         atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
576                           aload0pos, count, found);
577     }
578 
atMethodCallCore2(CtClass targetClass, String mname, boolean isStatic, boolean isSpecial, int aload0pos, int count, MemberResolver.Method found)579     private void atMethodCallCore2(CtClass targetClass, String mname,
580                                    boolean isStatic, boolean isSpecial,
581                                    int aload0pos, int count,
582                                    MemberResolver.Method found)
583         throws CompileError
584     {
585         CtClass declClass = found.declaring;
586         MethodInfo minfo = found.info;
587         String desc = minfo.getDescriptor();
588         int acc = minfo.getAccessFlags();
589 
590         if (mname.equals(MethodInfo.nameInit)) {
591             isSpecial = true;
592             if (declClass != targetClass)
593                 throw new CompileError("no such constructor");
594 
595             if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
596                 desc = getAccessibleConstructor(desc, declClass, minfo);
597                 bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
598             }
599         }
600         else if (AccessFlag.isPrivate(acc))
601             if (declClass == thisClass)
602                 isSpecial = true;
603             else {
604                 isSpecial = false;
605                 isStatic = true;
606                 String origDesc = desc;
607                 if ((acc & AccessFlag.STATIC) == 0)
608                     desc = Descriptor.insertParameter(declClass.getName(),
609                                                       origDesc);
610 
611                 acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
612                 mname = getAccessiblePrivate(mname, origDesc, desc,
613                                              minfo, declClass);
614             }
615 
616         boolean popTarget = false;
617         if ((acc & AccessFlag.STATIC) != 0) {
618             if (!isStatic) {
619                 /* this method is static but the target object is
620                    on stack.  It must be popped out.  If aload0pos >= 0,
621                    then the target object was pushed by aload_0.  It is
622                    overwritten by NOP.
623                 */
624                 isStatic = true;
625                 if (aload0pos >= 0)
626                     bytecode.write(aload0pos, NOP);
627                 else
628                     popTarget = true;
629             }
630 
631             bytecode.addInvokestatic(declClass, mname, desc);
632         }
633         else if (isSpecial)    // if (isSpecial && notStatic(acc))
634             bytecode.addInvokespecial(declClass, mname, desc);
635         else {
636             if (!Modifier.isPublic(declClass.getModifiers())
637                 || declClass.isInterface() != targetClass.isInterface())
638                 declClass = targetClass;
639 
640             if (declClass.isInterface())
641                 bytecode.addInvokeinterface(declClass, mname, desc, count);
642             else
643                 if (isStatic)
644                     throw new CompileError(mname + " is not static");
645                 else
646                     bytecode.addInvokevirtual(declClass, mname, desc);
647         }
648 
649         setReturnType(desc, isStatic, popTarget);
650     }
651 
652     /*
653      * Finds (or adds if necessary) a hidden accessor if the method
654      * is in an enclosing class.
655      *
656      * @param desc          the descriptor of the method.
657      * @param declClass     the class declaring the method.
658      */
getAccessiblePrivate(String methodName, String desc, String newDesc, MethodInfo minfo, CtClass declClass)659     protected String getAccessiblePrivate(String methodName, String desc,
660                                           String newDesc, MethodInfo minfo,
661                                           CtClass declClass)
662         throws CompileError
663     {
664         if (isEnclosing(declClass, thisClass)) {
665             AccessorMaker maker = declClass.getAccessorMaker();
666             if (maker != null)
667                 return maker.getMethodAccessor(methodName, desc, newDesc,
668                                                minfo);
669         }
670 
671         throw new CompileError("Method " + methodName
672                                + " is private");
673     }
674 
675     /*
676      * Finds (or adds if necessary) a hidden constructor if the given
677      * constructor is in an enclosing class.
678      *
679      * @param desc          the descriptor of the constructor.
680      * @param declClass     the class declaring the constructor.
681      * @param minfo         the method info of the constructor.
682      * @return the descriptor of the hidden constructor.
683      */
getAccessibleConstructor(String desc, CtClass declClass, MethodInfo minfo)684     protected String getAccessibleConstructor(String desc, CtClass declClass,
685                                               MethodInfo minfo)
686         throws CompileError
687     {
688         if (isEnclosing(declClass, thisClass)) {
689             AccessorMaker maker = declClass.getAccessorMaker();
690             if (maker != null)
691                 return maker.getConstructor(declClass, desc, minfo);
692         }
693 
694         throw new CompileError("the called constructor is private in "
695                                + declClass.getName());
696     }
697 
isEnclosing(CtClass outer, CtClass inner)698     private boolean isEnclosing(CtClass outer, CtClass inner) {
699         try {
700             while (inner != null) {
701                 inner = inner.getDeclaringClass();
702                 if (inner == outer)
703                     return true;
704             }
705         }
706         catch (NotFoundException e) {}
707         return false;
708     }
709 
getMethodArgsLength(ASTList args)710     public int getMethodArgsLength(ASTList args) {
711         return ASTList.length(args);
712     }
713 
atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames)714     public void atMethodArgs(ASTList args, int[] types, int[] dims,
715                              String[] cnames) throws CompileError {
716         int i = 0;
717         while (args != null) {
718             ASTree a = args.head();
719             a.accept(this);
720             types[i] = exprType;
721             dims[i] = arrayDim;
722             cnames[i] = className;
723             ++i;
724             args = args.tail();
725         }
726     }
727 
setReturnType(String desc, boolean isStatic, boolean popTarget)728     void setReturnType(String desc, boolean isStatic, boolean popTarget)
729         throws CompileError
730     {
731         int i = desc.indexOf(')');
732         if (i < 0)
733             badMethod();
734 
735         char c = desc.charAt(++i);
736         int dim = 0;
737         while (c == '[') {
738             ++dim;
739             c = desc.charAt(++i);
740         }
741 
742         arrayDim = dim;
743         if (c == 'L') {
744             int j = desc.indexOf(';', i + 1);
745             if (j < 0)
746                 badMethod();
747 
748             exprType = CLASS;
749             className = desc.substring(i + 1, j);
750         }
751         else {
752             exprType = MemberResolver.descToType(c);
753             className = null;
754         }
755 
756         int etype = exprType;
757         if (isStatic) {
758             if (popTarget) {
759                 if (is2word(etype, dim)) {
760                     bytecode.addOpcode(DUP2_X1);
761                     bytecode.addOpcode(POP2);
762                     bytecode.addOpcode(POP);
763                 }
764                 else if (etype == VOID)
765                     bytecode.addOpcode(POP);
766                 else {
767                     bytecode.addOpcode(SWAP);
768                     bytecode.addOpcode(POP);
769                 }
770             }
771         }
772     }
773 
atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup)774     protected void atFieldAssign(Expr expr, int op, ASTree left,
775                         ASTree right, boolean doDup) throws CompileError
776     {
777         CtField f = fieldAccess(left, false);
778         boolean is_static = resultStatic;
779         if (op != '=' && !is_static)
780             bytecode.addOpcode(DUP);
781 
782         int fi;
783         if (op == '=') {
784             FieldInfo finfo = f.getFieldInfo2();
785             setFieldType(finfo);
786             AccessorMaker maker = isAccessibleField(f, finfo);
787             if (maker == null)
788                 fi = addFieldrefInfo(f, finfo);
789             else
790                 fi = 0;
791         }
792         else
793             fi = atFieldRead(f, is_static);
794 
795         int fType = exprType;
796         int fDim = arrayDim;
797         String cname = className;
798 
799         atAssignCore(expr, op, right, fType, fDim, cname);
800 
801         boolean is2w = is2word(fType, fDim);
802         if (doDup) {
803             int dup_code;
804             if (is_static)
805                 dup_code = (is2w ? DUP2 : DUP);
806             else
807                 dup_code = (is2w ? DUP2_X1 : DUP_X1);
808 
809             bytecode.addOpcode(dup_code);
810         }
811 
812         atFieldAssignCore(f, is_static, fi, is2w);
813 
814         exprType = fType;
815         arrayDim = fDim;
816         className = cname;
817     }
818 
819     /* If fi == 0, the field must be a private field in an enclosing class.
820      */
atFieldAssignCore(CtField f, boolean is_static, int fi, boolean is2byte)821     private void atFieldAssignCore(CtField f, boolean is_static, int fi,
822                                    boolean is2byte) throws CompileError {
823         if (fi != 0) {
824             if (is_static) {
825                bytecode.add(PUTSTATIC);
826                bytecode.growStack(is2byte ? -2 : -1);
827             }
828             else {
829                 bytecode.add(PUTFIELD);
830                 bytecode.growStack(is2byte ? -3 : -2);
831             }
832 
833             bytecode.addIndex(fi);
834         }
835         else {
836             CtClass declClass = f.getDeclaringClass();
837             AccessorMaker maker = declClass.getAccessorMaker();
838             // make should be non null.
839             FieldInfo finfo = f.getFieldInfo2();
840             MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
841             bytecode.addInvokestatic(declClass, minfo.getName(),
842                                      minfo.getDescriptor());
843         }
844     }
845 
846     /* overwritten in JvstCodeGen.
847      */
atMember(Member mem)848     public void atMember(Member mem) throws CompileError {
849         atFieldRead(mem);
850     }
851 
atFieldRead(ASTree expr)852     protected void atFieldRead(ASTree expr) throws CompileError
853     {
854         CtField f = fieldAccess(expr, true);
855         if (f == null) {
856             atArrayLength(expr);
857             return;
858         }
859 
860         boolean is_static = resultStatic;
861         ASTree cexpr = TypeChecker.getConstantFieldValue(f);
862         if (cexpr == null)
863             atFieldRead(f, is_static);
864         else {
865             cexpr.accept(this);
866             setFieldType(f.getFieldInfo2());
867         }
868     }
869 
atArrayLength(ASTree expr)870     private void atArrayLength(ASTree expr) throws CompileError {
871         if (arrayDim == 0)
872             throw new CompileError(".length applied to a non array");
873 
874         bytecode.addOpcode(ARRAYLENGTH);
875         exprType = INT;
876         arrayDim = 0;
877     }
878 
879     /**
880      * Generates bytecode for reading a field value.
881      * It returns a fieldref_info index or zero if the field is a private
882      * one declared in an enclosing class.
883      */
atFieldRead(CtField f, boolean isStatic)884     private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
885         FieldInfo finfo = f.getFieldInfo2();
886         boolean is2byte = setFieldType(finfo);
887         AccessorMaker maker = isAccessibleField(f, finfo);
888         if (maker != null) {
889             MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
890             bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
891                                      minfo.getDescriptor());
892             return 0;
893         }
894         else {
895             int fi = addFieldrefInfo(f, finfo);
896             if (isStatic) {
897                 bytecode.add(GETSTATIC);
898                 bytecode.growStack(is2byte ? 2 : 1);
899             }
900             else {
901                 bytecode.add(GETFIELD);
902                 bytecode.growStack(is2byte ? 1 : 0);
903             }
904 
905             bytecode.addIndex(fi);
906             return fi;
907         }
908     }
909 
910     /**
911      * Returns null if the field is accessible.  Otherwise, it throws
912      * an exception or it returns AccessorMaker if the field is a private
913      * one declared in an enclosing class.
914      */
isAccessibleField(CtField f, FieldInfo finfo)915     private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
916         throws CompileError
917     {
918         if (AccessFlag.isPrivate(finfo.getAccessFlags())
919             && f.getDeclaringClass() != thisClass) {
920             CtClass declClass = f.getDeclaringClass();
921             if (isEnclosing(declClass, thisClass)) {
922                 AccessorMaker maker = declClass.getAccessorMaker();
923                 if (maker != null)
924                     return maker;
925                 else
926                     throw new CompileError("fatal error.  bug?");
927             }
928             else
929                 throw new CompileError("Field " + f.getName() + " in "
930                                        + declClass.getName() + " is private.");
931         }
932 
933         return null;    // accessible field
934     }
935 
936     /**
937      * Sets exprType, arrayDim, and className.
938      *
939      * @return true if the field type is long or double.
940      */
setFieldType(FieldInfo finfo)941     private boolean setFieldType(FieldInfo finfo) throws CompileError {
942         String type = finfo.getDescriptor();
943 
944         int i = 0;
945         int dim = 0;
946         char c = type.charAt(i);
947         while (c == '[') {
948             ++dim;
949             c = type.charAt(++i);
950         }
951 
952         arrayDim = dim;
953         exprType = MemberResolver.descToType(c);
954 
955         if (c == 'L')
956             className = type.substring(i + 1, type.indexOf(';', i + 1));
957         else
958             className = null;
959 
960         boolean is2byte = (c == 'J' || c == 'D');
961         return is2byte;
962     }
963 
addFieldrefInfo(CtField f, FieldInfo finfo)964     private int addFieldrefInfo(CtField f, FieldInfo finfo) {
965         ConstPool cp = bytecode.getConstPool();
966         String cname = f.getDeclaringClass().getName();
967         int ci = cp.addClassInfo(cname);
968         String name = finfo.getName();
969         String type = finfo.getDescriptor();
970         return cp.addFieldrefInfo(ci, name, type);
971     }
972 
atClassObject2(String cname)973     protected void atClassObject2(String cname) throws CompileError {
974         if (getMajorVersion() < ClassFile.JAVA_5)
975             super.atClassObject2(cname);
976         else
977             bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
978     }
979 
atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup)980     protected void atFieldPlusPlus(int token, boolean isPost,
981                                    ASTree oprand, Expr expr, boolean doDup)
982         throws CompileError
983     {
984         CtField f = fieldAccess(oprand, false);
985         boolean is_static = resultStatic;
986         if (!is_static)
987             bytecode.addOpcode(DUP);
988 
989         int fi = atFieldRead(f, is_static);
990         int t = exprType;
991         boolean is2w = is2word(t, arrayDim);
992 
993         int dup_code;
994         if (is_static)
995             dup_code = (is2w ? DUP2 : DUP);
996         else
997             dup_code = (is2w ? DUP2_X1 : DUP_X1);
998 
999         atPlusPlusCore(dup_code, doDup, token, isPost, expr);
1000         atFieldAssignCore(f, is_static, fi, is2w);
1001     }
1002 
1003     /* This method also returns a value in resultStatic.
1004      *
1005      * @param acceptLength      true if array length is acceptable
1006      */
fieldAccess(ASTree expr, boolean acceptLength)1007     protected CtField fieldAccess(ASTree expr, boolean acceptLength)
1008             throws CompileError
1009     {
1010         if (expr instanceof Member) {
1011             String name = ((Member)expr).get();
1012             CtField f = null;
1013             try {
1014                 f = thisClass.getField(name);
1015             }
1016             catch (NotFoundException e) {
1017                 // EXPR might be part of a static member access?
1018                 throw new NoFieldException(name, expr);
1019             }
1020 
1021             boolean is_static = Modifier.isStatic(f.getModifiers());
1022             if (!is_static)
1023                 if (inStaticMethod)
1024                     throw new CompileError(
1025                                 "not available in a static method: " + name);
1026                 else
1027                     bytecode.addAload(0);       // this
1028 
1029             resultStatic = is_static;
1030             return f;
1031         }
1032         else if (expr instanceof Expr) {
1033             Expr e = (Expr)expr;
1034             int op = e.getOperator();
1035             if (op == MEMBER) {
1036                 /* static member by # (extension by Javassist)
1037                  * For example, if int.class is parsed, the resulting tree
1038                  * is (# "java.lang.Integer" "TYPE").
1039                  */
1040                 CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(),
1041                                          (Symbol)e.oprand2());
1042                 resultStatic = true;
1043                 return f;
1044             }
1045             else if (op == '.') {
1046                 CtField f = null;
1047                 try {
1048                     e.oprand1().accept(this);
1049                     /* Don't call lookupFieldByJvmName2().
1050                      * The left operand of . is not a class name but
1051                      * a normal expression.
1052                      */
1053                     if (exprType == CLASS && arrayDim == 0)
1054                         f = resolver.lookupFieldByJvmName(className,
1055                                                     (Symbol)e.oprand2());
1056                     else if (acceptLength && arrayDim > 0
1057                              && ((Symbol)e.oprand2()).get().equals("length"))
1058                         return null;    // expr is an array length.
1059                     else
1060                         badLvalue();
1061 
1062                     boolean is_static = Modifier.isStatic(f.getModifiers());
1063                     if (is_static)
1064                         bytecode.addOpcode(POP);
1065 
1066                     resultStatic = is_static;
1067                     return f;
1068                 }
1069                 catch (NoFieldException nfe) {
1070                     if (nfe.getExpr() != e.oprand1())
1071                         throw nfe;
1072 
1073                     /* EXPR should be a static field.
1074                      * If EXPR might be part of a qualified class name,
1075                      * lookupFieldByJvmName2() throws NoFieldException.
1076                      */
1077                     Symbol fname = (Symbol)e.oprand2();
1078                     String cname = nfe.getField();
1079                     f = resolver.lookupFieldByJvmName2(cname, fname, expr);
1080                     resolver.recordPackage(cname);
1081                     resultStatic = true;
1082                     return f;
1083                 }
1084             }
1085             else
1086                 badLvalue();
1087         }
1088         else
1089             badLvalue();
1090 
1091         resultStatic = false;
1092         return null;    // never reach
1093     }
1094 
badLvalue()1095     private static void badLvalue() throws CompileError {
1096         throw new CompileError("bad l-value");
1097     }
1098 
makeParamList(MethodDecl md)1099     public CtClass[] makeParamList(MethodDecl md) throws CompileError {
1100         CtClass[] params;
1101         ASTList plist = md.getParams();
1102         if (plist == null)
1103             params = new CtClass[0];
1104         else {
1105             int i = 0;
1106             params = new CtClass[plist.length()];
1107             while (plist != null) {
1108                 params[i++] = resolver.lookupClass((Declarator)plist.head());
1109                 plist = plist.tail();
1110             }
1111         }
1112 
1113         return params;
1114     }
1115 
makeThrowsList(MethodDecl md)1116     public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
1117         CtClass[] clist;
1118         ASTList list = md.getThrows();
1119         if (list == null)
1120             return null;
1121         else {
1122             int i = 0;
1123             clist = new CtClass[list.length()];
1124             while (list != null) {
1125                 clist[i++] = resolver.lookupClassByName((ASTList)list.head());
1126                 list = list.tail();
1127             }
1128 
1129             return clist;
1130         }
1131     }
1132 
1133     /* Converts a class name into a JVM-internal representation.
1134      *
1135      * It may also expand a simple class name to java.lang.*.
1136      * For example, this converts Object into java/lang/Object.
1137      */
resolveClassName(ASTList name)1138     protected String resolveClassName(ASTList name) throws CompileError {
1139         return resolver.resolveClassName(name);
1140     }
1141 
1142     /* Expands a simple class name to java.lang.*.
1143      * For example, this converts Object into java/lang/Object.
1144      */
resolveClassName(String jvmName)1145     protected String resolveClassName(String jvmName) throws CompileError {
1146         return resolver.resolveJvmClassName(jvmName);
1147     }
1148 }
1149