/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