001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.expr;
017:
018: import javassist.*;
019: import javassist.bytecode.*;
020: import javassist.compiler.*;
021: import javassist.compiler.ast.ASTList;
022:
023: /**
024: * Expression for accessing a field.
025: */
026: public class FieldAccess extends Expr {
027: int opcode;
028:
029: protected FieldAccess(int pos, CodeIterator i, CtClass declaring,
030: MethodInfo m, int op) {
031: super (pos, i, declaring, m);
032: opcode = op;
033: }
034:
035: /**
036: * Returns the method or constructor containing the field-access
037: * expression represented by this object.
038: */
039: public CtBehavior where() {
040: return super .where();
041: }
042:
043: /**
044: * Returns the line number of the source line containing the
045: * field access.
046: *
047: * @return -1 if this information is not available.
048: */
049: public int getLineNumber() {
050: return super .getLineNumber();
051: }
052:
053: /**
054: * Returns the source file containing the field access.
055: *
056: * @return null if this information is not available.
057: */
058: public String getFileName() {
059: return super .getFileName();
060: }
061:
062: /**
063: * Returns true if the field is static.
064: */
065: public boolean isStatic() {
066: return isStatic(opcode);
067: }
068:
069: static boolean isStatic(int c) {
070: return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC;
071: }
072:
073: /**
074: * Returns true if the field is read.
075: */
076: public boolean isReader() {
077: return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC;
078: }
079:
080: /**
081: * Returns true if the field is written in.
082: */
083: public boolean isWriter() {
084: return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC;
085: }
086:
087: /**
088: * Returns the class in which the field is declared.
089: */
090: private CtClass getCtClass() throws NotFoundException {
091: return this Class.getClassPool().get(getClassName());
092: }
093:
094: /**
095: * Returns the name of the class in which the field is declared.
096: */
097: public String getClassName() {
098: int index = iterator.u16bitAt(currentPos + 1);
099: 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 signature of the field type.
130: * The signature is represented by a character string
131: * called field descriptor, which is defined in the JVM specification.
132: *
133: * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool)
134: * @since 3.1
135: */
136: public String getSignature() {
137: int index = iterator.u16bitAt(currentPos + 1);
138: return getConstPool().getFieldrefType(index);
139: }
140:
141: /**
142: * Replaces the method call with the bytecode derived from
143: * the given source text.
144: *
145: * <p>$0 is available even if the called method is static.
146: * If the field access is writing, $_ is available but the value
147: * of $_ is ignored.
148: *
149: * @param statement a Java statement.
150: */
151: public void replace(String statement) throws CannotCompileException {
152: ConstPool constPool = getConstPool();
153: int pos = currentPos;
154: int index = iterator.u16bitAt(pos + 1);
155:
156: Javac jc = new Javac(this Class);
157: CodeAttribute ca = iterator.get();
158: try {
159: CtClass[] params;
160: CtClass retType;
161: CtClass fieldType = Descriptor.toCtClass(constPool
162: .getFieldrefType(index), this Class.getClassPool());
163: boolean read = isReader();
164: if (read) {
165: params = new CtClass[0];
166: retType = fieldType;
167: } else {
168: params = new CtClass[1];
169: params[0] = fieldType;
170: retType = CtClass.voidType;
171: }
172:
173: int paramVar = ca.getMaxLocals();
174: jc.recordParams(constPool.getFieldrefClassName(index),
175: params, true, paramVar, withinStatic());
176:
177: /* Is $_ included in the source code?
178: */
179: boolean included = checkResultValue(retType, statement);
180: if (read)
181: included = true;
182:
183: int retVar = jc.recordReturnType(retType, included);
184: if (read)
185: jc.recordProceed(new ProceedForRead(retType, opcode,
186: index, paramVar));
187: else {
188: // because $type is not the return type...
189: jc.recordType(fieldType);
190: jc.recordProceed(new ProceedForWrite(params[0], opcode,
191: index, paramVar));
192: }
193:
194: Bytecode bytecode = jc.getBytecode();
195: storeStack(params, isStatic(), paramVar, bytecode);
196: jc.recordLocalVariables(ca, pos);
197:
198: if (included)
199: if (retType == CtClass.voidType) {
200: bytecode.addOpcode(ACONST_NULL);
201: bytecode.addAstore(retVar);
202: } else {
203: bytecode.addConstZero(retType);
204: bytecode.addStore(retVar, retType); // initialize $_
205: }
206:
207: jc.compileStmnt(statement);
208: if (read)
209: bytecode.addLoad(retVar, retType);
210:
211: replace0(pos, bytecode, 3);
212: } catch (CompileError e) {
213: throw new CannotCompileException(e);
214: } catch (NotFoundException e) {
215: throw new CannotCompileException(e);
216: } catch (BadBytecode e) {
217: throw new CannotCompileException("broken method");
218: }
219: }
220:
221: /* <field type> $proceed()
222: */
223: static class ProceedForRead implements ProceedHandler {
224: CtClass fieldType;
225: int opcode;
226: int targetVar, index;
227:
228: ProceedForRead(CtClass type, int op, int i, int var) {
229: fieldType = type;
230: targetVar = var;
231: opcode = op;
232: index = i;
233: }
234:
235: public void doit(JvstCodeGen gen, Bytecode bytecode,
236: ASTList args) throws CompileError {
237: if (args != null && !gen.isParamListName(args))
238: throw new CompileError(
239: Javac.proceedName
240: + "() cannot take a parameter for field reading");
241:
242: int stack;
243: if (isStatic(opcode))
244: stack = 0;
245: else {
246: stack = -1;
247: bytecode.addAload(targetVar);
248: }
249:
250: if (fieldType instanceof CtPrimitiveType)
251: stack += ((CtPrimitiveType) fieldType).getDataSize();
252: else
253: ++stack;
254:
255: bytecode.add(opcode);
256: bytecode.addIndex(index);
257: bytecode.growStack(stack);
258: gen.setType(fieldType);
259: }
260:
261: public void setReturnType(JvstTypeChecker c, ASTList args)
262: throws CompileError {
263: c.setType(fieldType);
264: }
265: }
266:
267: /* void $proceed(<field type>)
268: * the return type is not the field type but void.
269: */
270: static class ProceedForWrite implements ProceedHandler {
271: CtClass fieldType;
272: int opcode;
273: int targetVar, index;
274:
275: ProceedForWrite(CtClass type, int op, int i, int var) {
276: fieldType = type;
277: targetVar = var;
278: opcode = op;
279: index = i;
280: }
281:
282: public void doit(JvstCodeGen gen, Bytecode bytecode,
283: ASTList args) throws CompileError {
284: if (gen.getMethodArgsLength(args) != 1)
285: throw new CompileError(Javac.proceedName
286: + "() cannot take more than one parameter "
287: + "for field writing");
288:
289: int stack;
290: if (isStatic(opcode))
291: stack = 0;
292: else {
293: stack = -1;
294: bytecode.addAload(targetVar);
295: }
296:
297: gen.atMethodArgs(args, new int[1], new int[1],
298: new String[1]);
299: gen.doNumCast(fieldType);
300: if (fieldType instanceof CtPrimitiveType)
301: stack -= ((CtPrimitiveType) fieldType).getDataSize();
302: else
303: --stack;
304:
305: bytecode.add(opcode);
306: bytecode.addIndex(index);
307: bytecode.growStack(stack);
308: gen.setType(CtClass.voidType);
309: gen.addNullIfVoid();
310: }
311:
312: public void setReturnType(JvstTypeChecker c, ASTList args)
313: throws CompileError {
314: c.atMethodArgs(args, new int[1], new int[1], new String[1]);
315: c.setType(CtClass.voidType);
316: c.addNullIfVoid();
317: }
318: }
319: }
|