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
16package javassist.compiler;
17
18import javassist.*;
19import javassist.bytecode.*;
20import javassist.compiler.ast.*;
21
22import java.util.ArrayList;
23
24/* Code generator methods depending on javassist.* classes.
25 */
26public class MemberCodeGen extends CodeGen {
27    protected MemberResolver resolver;
28    protected CtClass   thisClass;
29    protected MethodInfo thisMethod;
30
31    protected boolean resultStatic;
32
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     */
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     */
55    public void setThisMethod(CtMethod m) {
56        thisMethod = m.getMethodInfo2();
57        if (typeChecker != null)
58            typeChecker.setThisMethod(thisMethod);
59    }
60
61    public CtClass getThisClass() { return thisClass; }
62
63    /**
64     * Returns the JVM-internal representation of this class name.
65     */
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     */
73    protected String getSuperName() throws CompileError {
74        return MemberResolver.javaToJvmName(
75                        MemberResolver.getSuperclass(thisClass).getName());
76    }
77
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
89        JsrHook(CodeGen gen) {
90            super(gen);
91            jsrList = new ArrayList();
92            cgen = gen;
93            var = -1;
94        }
95
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
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
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
153        JsrHook2(CodeGen gen, int[] retTarget) {
154            super(gen);
155            target = retTarget[0];
156            var = retTarget[1];
157        }
158
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
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     */
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
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
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
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
406    private static void badNewExpr() throws CompileError {
407        throw new CompileError("bad new expression");
408    }
409
410    protected void atArrayVariableAssign(ArrayInit init, int varType,
411                                         int varArray, String varClass) throws CompileError {
412        atNewArrayExpr2(varType, null, varClass, init);
413    }
414
415    public void atArrayInit(ArrayInit init) throws CompileError {
416        throw new CompileError("array initializer is not supported");
417    }
418
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
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
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     */
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
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     */
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     */
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
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
710    public int getMethodArgsLength(ASTList args) {
711        return ASTList.length(args);
712    }
713
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
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
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     */
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     */
848    public void atMember(Member mem) throws CompileError {
849        atFieldRead(mem);
850    }
851
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
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     */
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     */
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     */
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
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
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
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     */
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
1095    private static void badLvalue() throws CompileError {
1096        throw new CompileError("bad l-value");
1097    }
1098
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
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     */
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     */
1145    protected String resolveClassName(String jvmName) throws CompileError {
1146        return resolver.resolveJvmClassName(jvmName);
1147    }
1148}
1149