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.expr;
18 
19 import javassist.CannotCompileException;
20 import javassist.ClassPool;
21 import javassist.CtBehavior;
22 import javassist.CtClass;
23 import javassist.CtField;
24 import javassist.CtPrimitiveType;
25 import javassist.NotFoundException;
26 import javassist.bytecode.BadBytecode;
27 import javassist.bytecode.Bytecode;
28 import javassist.bytecode.CodeAttribute;
29 import javassist.bytecode.CodeIterator;
30 import javassist.bytecode.ConstPool;
31 import javassist.bytecode.Descriptor;
32 import javassist.bytecode.MethodInfo;
33 import javassist.bytecode.Opcode;
34 import javassist.compiler.CompileError;
35 import javassist.compiler.Javac;
36 import javassist.compiler.JvstCodeGen;
37 import javassist.compiler.JvstTypeChecker;
38 import javassist.compiler.ProceedHandler;
39 import javassist.compiler.ast.ASTList;
40 
41 /**
42  * Expression for accessing a field.
43  */
44 public class FieldAccess extends Expr {
45     int opcode;
46 
FieldAccess(int pos, CodeIterator i, CtClass declaring, MethodInfo m, int op)47     protected FieldAccess(int pos, CodeIterator i, CtClass declaring,
48                           MethodInfo m, int op) {
49         super(pos, i, declaring, m);
50         opcode = op;
51     }
52 
53     /**
54      * Returns the method or constructor containing the field-access
55      * expression represented by this object.
56      */
57     @Override
where()58     public CtBehavior where() { return super.where(); }
59 
60     /**
61      * Returns the line number of the source line containing the
62      * field access.
63      *
64      * @return -1       if this information is not available.
65      */
66     @Override
getLineNumber()67     public int getLineNumber() {
68         return super.getLineNumber();
69     }
70 
71     /**
72      * Returns the source file containing the field access.
73      *
74      * @return null     if this information is not available.
75      */
76     @Override
getFileName()77     public String getFileName() {
78         return super.getFileName();
79     }
80 
81     /**
82      * Returns true if the field is static.
83      */
isStatic()84     public boolean isStatic() {
85         return isStatic(opcode);
86     }
87 
isStatic(int c)88     static boolean isStatic(int c) {
89         return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC;
90     }
91 
92     /**
93      * Returns true if the field is read.
94      */
isReader()95     public boolean isReader() {
96         return opcode == Opcode.GETFIELD || opcode ==  Opcode.GETSTATIC;
97     }
98 
99     /**
100      * Returns true if the field is written in.
101      */
isWriter()102     public boolean isWriter() {
103         return opcode == Opcode.PUTFIELD || opcode ==  Opcode.PUTSTATIC;
104     }
105 
106     /**
107      * Returns the class in which the field is declared.
108      */
getCtClass()109     private CtClass getCtClass() throws NotFoundException {
110         return thisClass.getClassPool().get(getClassName());
111     }
112 
113     /**
114      * Returns the name of the class in which the field is declared.
115      */
getClassName()116     public String getClassName() {
117         int index = iterator.u16bitAt(currentPos + 1);
118         return getConstPool().getFieldrefClassName(index);
119     }
120 
121     /**
122      * Returns the name of the field.
123      */
getFieldName()124     public String getFieldName() {
125         int index = iterator.u16bitAt(currentPos + 1);
126         return getConstPool().getFieldrefName(index);
127     }
128 
129     /**
130      * Returns the field accessed by this expression.
131      */
getField()132     public CtField getField() throws NotFoundException {
133         CtClass cc = getCtClass();
134         int index = iterator.u16bitAt(currentPos + 1);
135         ConstPool cp = getConstPool();
136         return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index));
137     }
138 
139     /**
140      * Returns the list of exceptions that the expression may throw.
141      * This list includes both the exceptions that the try-catch statements
142      * including the expression can catch and the exceptions that
143      * the throws declaration allows the method to throw.
144      */
145     @Override
mayThrow()146     public CtClass[] mayThrow() {
147         return super.mayThrow();
148     }
149 
150     /**
151      * Returns the signature of the field type.
152      * The signature is represented by a character string
153      * called field descriptor, which is defined in the JVM specification.
154      *
155      * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool)
156      * @since 3.1
157      */
getSignature()158     public String getSignature() {
159         int index = iterator.u16bitAt(currentPos + 1);
160         return getConstPool().getFieldrefType(index);
161     }
162 
163     /**
164      * Replaces the method call with the bytecode derived from
165      * the given source text.
166      *
167      * <p>$0 is available even if the called method is static.
168      * If the field access is writing, $_ is available but the value
169      * of $_ is ignored.
170      *
171      * @param statement         a Java statement except try-catch.
172      */
173     @Override
replace(String statement)174     public void replace(String statement) throws CannotCompileException {
175         thisClass.getClassFile();   // to call checkModify().
176         ConstPool constPool = getConstPool();
177         int pos = currentPos;
178         int index = iterator.u16bitAt(pos + 1);
179 
180         Javac jc = new Javac(thisClass);
181         CodeAttribute ca = iterator.get();
182         try {
183             CtClass[] params;
184             CtClass retType;
185             CtClass fieldType
186                 = Descriptor.toCtClass(constPool.getFieldrefType(index),
187                                        thisClass.getClassPool());
188             boolean read = isReader();
189             if (read) {
190                 params = new CtClass[0];
191                 retType = fieldType;
192             }
193             else {
194                 params = new CtClass[1];
195                 params[0] = fieldType;
196                 retType = CtClass.voidType;
197             }
198 
199             int paramVar = ca.getMaxLocals();
200             jc.recordParams(constPool.getFieldrefClassName(index), params,
201                             true, paramVar, withinStatic());
202 
203             /* Is $_ included in the source code?
204              */
205             boolean included = checkResultValue(retType, statement);
206             if (read)
207                 included = true;
208 
209             int retVar = jc.recordReturnType(retType, included);
210             if (read)
211                 jc.recordProceed(new ProceedForRead(retType, opcode,
212                                                     index, paramVar));
213             else {
214                 // because $type is not the return type...
215                 jc.recordType(fieldType);
216                 jc.recordProceed(new ProceedForWrite(params[0], opcode,
217                                                      index, paramVar));
218             }
219 
220             Bytecode bytecode = jc.getBytecode();
221             storeStack(params, isStatic(), paramVar, bytecode);
222             jc.recordLocalVariables(ca, pos);
223 
224             if (included)
225                 if (retType == CtClass.voidType) {
226                     bytecode.addOpcode(ACONST_NULL);
227                     bytecode.addAstore(retVar);
228                 }
229                 else {
230                     bytecode.addConstZero(retType);
231                     bytecode.addStore(retVar, retType);     // initialize $_
232                 }
233 
234             jc.compileStmnt(statement);
235             if (read)
236                 bytecode.addLoad(retVar, retType);
237 
238             replace0(pos, bytecode, 3);
239         }
240         catch (CompileError e) { throw new CannotCompileException(e); }
241         catch (NotFoundException e) { throw new CannotCompileException(e); }
242         catch (BadBytecode e) {
243             throw new CannotCompileException("broken method");
244         }
245     }
246 
247     /* <field type> $proceed()
248      */
249     static class ProceedForRead implements ProceedHandler {
250         CtClass fieldType;
251         int opcode;
252         int targetVar, index;
253 
ProceedForRead(CtClass type, int op, int i, int var)254         ProceedForRead(CtClass type, int op, int i, int var) {
255             fieldType = type;
256             targetVar = var;
257             opcode = op;
258             index = i;
259         }
260 
261         @Override
doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)262         public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
263             throws CompileError
264         {
265             if (args != null && !gen.isParamListName(args))
266                 throw new CompileError(Javac.proceedName
267                         + "() cannot take a parameter for field reading");
268 
269             int stack;
270             if (isStatic(opcode))
271                 stack = 0;
272             else {
273                 stack = -1;
274                 bytecode.addAload(targetVar);
275             }
276 
277             if (fieldType instanceof CtPrimitiveType)
278                 stack += ((CtPrimitiveType)fieldType).getDataSize();
279             else
280                 ++stack;
281 
282             bytecode.add(opcode);
283             bytecode.addIndex(index);
284             bytecode.growStack(stack);
285             gen.setType(fieldType);
286         }
287 
288         @Override
setReturnType(JvstTypeChecker c, ASTList args)289         public void setReturnType(JvstTypeChecker c, ASTList args)
290             throws CompileError
291         {
292             c.setType(fieldType);
293         }
294     }
295 
296     /* void $proceed(<field type>)
297      *          the return type is not the field type but void.
298      */
299     static class ProceedForWrite implements ProceedHandler {
300         CtClass fieldType;
301         int opcode;
302         int targetVar, index;
303 
ProceedForWrite(CtClass type, int op, int i, int var)304         ProceedForWrite(CtClass type, int op, int i, int var) {
305             fieldType = type;
306             targetVar = var;
307             opcode = op;
308             index = i;
309         }
310 
311         @Override
doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)312         public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
313             throws CompileError
314         {
315             if (gen.getMethodArgsLength(args) != 1)
316                 throw new CompileError(Javac.proceedName
317                         + "() cannot take more than one parameter "
318                         + "for field writing");
319 
320             int stack;
321             if (isStatic(opcode))
322                 stack = 0;
323             else {
324                 stack = -1;
325                 bytecode.addAload(targetVar);
326             }
327 
328             gen.atMethodArgs(args, new int[1], new int[1], new String[1]);
329             gen.doNumCast(fieldType);
330             if (fieldType instanceof CtPrimitiveType)
331                 stack -= ((CtPrimitiveType)fieldType).getDataSize();
332             else
333                 --stack;
334 
335             bytecode.add(opcode);
336             bytecode.addIndex(index);
337             bytecode.growStack(stack);
338             gen.setType(CtClass.voidType);
339             gen.addNullIfVoid();
340         }
341 
342         @Override
setReturnType(JvstTypeChecker c, ASTList args)343         public void setReturnType(JvstTypeChecker c, ASTList args)
344             throws CompileError
345         {
346             c.atMethodArgs(args, new int[1], new int[1], new String[1]);
347             c.setType(CtClass.voidType);
348             c.addNullIfVoid();
349         }
350     }
351 }
352