/Users/lyon/j4p/src/javassist/expr/FieldAccess.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 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    
29       FieldAccess(int pos, CodeIterator i, CtClass declaring, MethodInfo m, 
30                   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        */ 
39       public CtBehavior where() { 
40           return super.where(); 
41       } 
42    
43       /** 
44        * Returns the line number of the source line containing the 
45        * field access. 
46        * 
47        * @return -1       if this information is not available. 
48        */ 
49       public int getLineNumber() { 
50           return super.getLineNumber(); 
51       } 
52    
53       /** 
54        * Returns the source file containing the field access. 
55        * 
56        * @return null     if this information is not available. 
57        */ 
58       public String getFileName() { 
59           return super.getFileName(); 
60       } 
61    
62       /** 
63        * Returns true if the field is static. 
64        */ 
65       public boolean isStatic() { 
66           return isStatic(opcode); 
67       } 
68    
69       static boolean isStatic(int c) { 
70           return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC; 
71       } 
72    
73       /** 
74        * Returns true if the field is read. 
75        */ 
76       public boolean isReader() { 
77           return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC; 
78       } 
79    
80       /** 
81        * Returns true if the field is written in. 
82        */ 
83       public boolean isWriter() { 
84           return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC; 
85       } 
86    
87       /** 
88        * Returns the class in which the field is declared. 
89        */ 
90       private CtClass getCtClass() throws NotFoundException { 
91           return thisClass.getClassPool().get(getClassName()); 
92       } 
93    
94       /** 
95        * Returns the name of the class in which the field is declared. 
96        */ 
97       public String getClassName() { 
98           int index = iterator.u16bitAt(currentPos + 1); 
99           return getConstPool().getFieldrefClassName(index); 
100      } 
101   
102      /** 
103       * Returns the name of the field. 
104       */ 
105      public String getFieldName() { 
106          int index = iterator.u16bitAt(currentPos + 1); 
107          return getConstPool().getFieldrefName(index); 
108      } 
109   
110      /** 
111       * Returns the field accessed by this expression. 
112       */ 
113      public CtField getField() throws NotFoundException { 
114          CtClass cc = getCtClass(); 
115          return cc.getField(getFieldName()); 
116      } 
117   
118      /** 
119       * Returns the list of exceptions that the expression may throw. 
120       * This list includes both the exceptions that the try-catch statements 
121       * including the expression can catch and the exceptions that 
122       * the throws declaration allows the method to throw. 
123       */ 
124      public CtClass[] mayThrow() { 
125          return super.mayThrow(); 
126      } 
127   
128      /* 
129       * Returns the type of the field. 
130   
131      public CtClass getFieldType() throws NotFoundException { 
132          int index = iterator.u16bitAt(currentPos + 1); 
133          String type = getConstPool().getFieldrefType(index); 
134          return Descriptor.toCtClass(type, thisClass.getClassPool()); 
135      } 
136      */ 
137   
138      /** 
139       * Replaces the method call with the bytecode derived from 
140       * the given source text. 
141       * 
142       * <p>$0 is available even if the called method is static. 
143       * If the field access is writing, $_ is available but the value 
144       * of $_ is ignored. 
145       * 
146       * @param statement         a Java statement. 
147       */ 
148      public void replace(String statement) throws CannotCompileException { 
149          ConstPool constPool = getConstPool(); 
150          int pos = currentPos; 
151          int index = iterator.u16bitAt(pos + 1); 
152   
153          Javac jc = new Javac(thisClass); 
154          CodeAttribute ca = iterator.get(); 
155          try { 
156              CtClass[] params; 
157              CtClass retType; 
158              CtClass fieldType 
159                      = Descriptor.toCtClass(constPool.getFieldrefType(index), 
160                              thisClass.getClassPool()); 
161              boolean read = isReader(); 
162              if (read) { 
163                  params = new CtClass[0]; 
164                  retType = fieldType; 
165              } else { 
166                  params = new CtClass[1]; 
167                  params[0] = fieldType; 
168                  retType = CtClass.voidType; 
169              } 
170   
171              int paramVar = ca.getMaxLocals(); 
172              jc.recordParams(constPool.getFieldrefClassName(index), params, 
173                      true, paramVar, withinStatic()); 
174   
175              /* Is $_ included in the source code? 
176               */ 
177              boolean included = checkResultValue(retType, statement); 
178   
179              int retVar = jc.recordReturnType(retType, included); 
180              if (read) 
181                  jc.recordProceed(new ProceedForRead(retType, opcode, 
182                          index, paramVar)); 
183              else { 
184                  // because $type is not the return type... 
185                  jc.recordType(fieldType); 
186                  jc.recordProceed(new ProceedForWrite(params[0], opcode, 
187                          index, paramVar)); 
188              } 
189   
190              Bytecode bytecode = jc.getBytecode(); 
191              storeStack(params, isStatic(), paramVar, bytecode); 
192              jc.compileStmnt(statement); 
193              if (read) 
194                  bytecode.addLoad(retVar, retType); 
195   
196              replace0(pos, bytecode, 3); 
197          } catch (CompileError e) { 
198              throw new CannotCompileException(e); 
199          } catch (NotFoundException e) { 
200              throw new CannotCompileException(e); 
201          } catch (BadBytecode e) { 
202              throw new CannotCompileException("broken method"); 
203          } 
204      } 
205   
206      /* <field type> $proceed() 
207       */ 
208      static class ProceedForRead implements ProceedHandler { 
209          CtClass fieldType; 
210          int opcode; 
211          int targetVar, index; 
212   
213          ProceedForRead(CtClass type, int op, int i, int var) { 
214              fieldType = type; 
215              targetVar = var; 
216              opcode = op; 
217              index = i; 
218          } 
219   
220          public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) 
221                  throws CompileError { 
222              if (args != null && !gen.isParamListName(args)) 
223                  throw new CompileError(Javac.proceedName 
224                          + "() cannot take a parameter for field reading"); 
225   
226              int stack; 
227              if (isStatic(opcode)) 
228                  stack = 0; 
229              else { 
230                  stack = -1; 
231                  bytecode.addAload(targetVar); 
232              } 
233   
234              if (fieldType instanceof CtPrimitiveType) 
235                  stack += ((CtPrimitiveType) fieldType).getDataSize(); 
236              else 
237                  ++stack; 
238   
239              bytecode.add(opcode); 
240              bytecode.addIndex(index); 
241              bytecode.growStack(stack); 
242              gen.setType(fieldType); 
243          } 
244      } 
245   
246      /* void $proceed(<field type>) 
247       *          the return type is not the field type but void. 
248       */ 
249      static class ProceedForWrite implements ProceedHandler { 
250          CtClass fieldType; 
251          int opcode; 
252          int targetVar, index; 
253   
254          ProceedForWrite(CtClass type, int op, int i, int var) { 
255              fieldType = type; 
256              targetVar = var; 
257              opcode = op; 
258              index = i; 
259          } 
260   
261          public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) 
262                  throws CompileError { 
263              if (gen.atMethodArgsLength(args) != 1) 
264                  throw new CompileError(Javac.proceedName 
265                          + "() cannot take more than one parameter " 
266                          + "for field writing"); 
267   
268              int stack; 
269              if (isStatic(opcode)) 
270                  stack = 0; 
271              else { 
272                  stack = -1; 
273                  bytecode.addAload(targetVar); 
274              } 
275   
276              gen.atMethodArgs(args, new int[1], new int[1], new String[1]); 
277              gen.doNumCast(fieldType); 
278              if (fieldType instanceof CtPrimitiveType) 
279                  stack -= ((CtPrimitiveType) fieldType).getDataSize(); 
280              else 
281                  --stack; 
282   
283              bytecode.add(opcode); 
284              bytecode.addIndex(index); 
285              bytecode.growStack(stack); 
286              gen.setType(CtClass.voidType); 
287              gen.addNullIfVoid(); 
288          } 
289      } 
290  } 
291