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