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