001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2003 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: int prevOpcode;
029:
030: FieldAccess(int pos, CodeIterator i, CtClass declaring,
031: MethodInfo m, int op, int prevOp) {
032: super (pos, i, declaring, m);
033: opcode = op;
034: prevOpcode = prevOp;
035: }
036:
037: /**
038: * Returns the method or constructor containing the field-access
039: * expression represented by this object.
040: */
041: public CtBehavior where() {
042: return super .where();
043: }
044:
045: /**
046: * Returns the line number of the source line containing the
047: * field access.
048: *
049: * @return -1 if this information is not available.
050: */
051: public int getLineNumber() {
052: return super .getLineNumber();
053: }
054:
055: /**
056: * Returns the source file containing the field access.
057: *
058: * @return null if this information is not available.
059: */
060: public String getFileName() {
061: return super .getFileName();
062: }
063:
064: /**
065: * Returns true if the field is static.
066: */
067: public boolean isStatic() {
068: return isStatic(opcode);
069: }
070:
071: static boolean isStatic(int c) {
072: return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC;
073: }
074:
075: /**
076: * Returns true if the field is read.
077: */
078: public boolean isReader() {
079: return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC;
080: }
081:
082: /**
083: * Returns true if the self instance field is read.
084: */
085: public boolean isSelfReader() {
086: return opcode == Opcode.GETFIELD
087: && prevOpcode == Opcode.ALOAD_0
088: && (this Method.getAccessFlags() & AccessFlag.STATIC) == 0;
089: }
090:
091: /**
092: * Returns true if the field is written in.
093: */
094: public boolean isWriter() {
095: return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC;
096: }
097:
098: /**
099: * Returns the class in which the field is declared.
100: */
101: private CtClass getCtClass() throws NotFoundException {
102: return this Class.getClassPool().get(getClassName());
103: }
104:
105: /**
106: * Returns the name of the class in which the field is declared.
107: */
108: public String getClassName() {
109: int index = iterator.u16bitAt(currentPos + 1);
110: return getConstPool().getFieldrefClassName(index);
111: }
112:
113: /**
114: * Returns the name of the field.
115: */
116: public String getFieldName() {
117: int index = iterator.u16bitAt(currentPos + 1);
118: return getConstPool().getFieldrefName(index);
119: }
120:
121: /**
122: * Returns the field accessed by this expression.
123: */
124: public CtField getField() throws NotFoundException {
125: CtClass cc = getCtClass();
126: return cc.getField(getFieldName());
127: }
128:
129: /**
130: * Returns the list of exceptions that the expression may throw.
131: * This list includes both the exceptions that the try-catch statements
132: * including the expression can catch and the exceptions that
133: * the throws declaration allows the method to throw.
134: */
135: public CtClass[] mayThrow() {
136: return super .mayThrow();
137: }
138:
139: /*
140: * Returns the type of the field.
141:
142: public CtClass getFieldType() throws NotFoundException {
143: int index = iterator.u16bitAt(currentPos + 1);
144: String type = getConstPool().getFieldrefType(index);
145: return Descriptor.toCtClass(type, thisClass.getClassPool());
146: }
147: */
148:
149: /**
150: * Replaces the method call with the bytecode derived from
151: * the given source text.
152: *
153: * <p>$0 is available even if the called method is static.
154: * If the field access is writing, $_ is available but the value
155: * of $_ is ignored.
156: *
157: * @param statement a Java statement.
158: */
159: public void replace(String statement) throws CannotCompileException {
160: ConstPool constPool = getConstPool();
161: int pos = currentPos;
162: int index = iterator.u16bitAt(pos + 1);
163:
164: Javac jc = new Javac(this Class);
165: CodeAttribute ca = iterator.get();
166: try {
167: CtClass[] params;
168: CtClass retType;
169: CtClass fieldType = Descriptor.toCtClass(constPool
170: .getFieldrefType(index), this Class.getClassPool());
171: boolean read = isReader();
172: if (read) {
173: params = new CtClass[0];
174: retType = fieldType;
175: } else {
176: params = new CtClass[1];
177: params[0] = fieldType;
178: retType = CtClass.voidType;
179: }
180:
181: int paramVar = ca.getMaxLocals();
182: jc.recordParams(constPool.getFieldrefClassName(index),
183: params, true, paramVar, withinStatic());
184:
185: /* Is $_ included in the source code?
186: */
187: boolean included = checkResultValue(retType, statement);
188:
189: int retVar = jc.recordReturnType(retType, included);
190: if (read)
191: jc.recordProceed(new ProceedForRead(retType, opcode,
192: index, paramVar));
193: else {
194: // because $type is not the return type...
195: jc.recordType(fieldType);
196: jc.recordProceed(new ProceedForWrite(params[0], opcode,
197: index, paramVar));
198: }
199:
200: Bytecode bytecode = jc.getBytecode();
201: storeStack(params, isStatic(), paramVar, bytecode);
202: jc.compileStmnt(statement);
203: if (read)
204: bytecode.addLoad(retVar, retType);
205:
206: replace0(pos, bytecode, 3);
207: } catch (CompileError e) {
208: throw new CannotCompileException(e);
209: } catch (NotFoundException e) {
210: throw new CannotCompileException(e);
211: } catch (BadBytecode e) {
212: throw new CannotCompileException("broken method");
213: }
214: }
215:
216: /* <field type> $proceed()
217: */
218: static class ProceedForRead implements ProceedHandler {
219: CtClass fieldType;
220: int opcode;
221: int targetVar, index;
222:
223: ProceedForRead(CtClass type, int op, int i, int var) {
224: fieldType = type;
225: targetVar = var;
226: opcode = op;
227: index = i;
228: }
229:
230: public void doit(JvstCodeGen gen, Bytecode bytecode,
231: ASTList args) throws CompileError {
232: if (args != null && !gen.isParamListName(args))
233: throw new CompileError(
234: Javac.proceedName
235: + "() cannot take a parameter for field reading");
236:
237: int stack;
238: if (isStatic(opcode))
239: stack = 0;
240: else {
241: stack = -1;
242: bytecode.addAload(targetVar);
243: }
244:
245: if (fieldType instanceof CtPrimitiveType)
246: stack += ((CtPrimitiveType) fieldType).getDataSize();
247: else
248: ++stack;
249:
250: bytecode.add(opcode);
251: bytecode.addIndex(index);
252: bytecode.growStack(stack);
253: gen.setType(fieldType);
254: }
255: }
256:
257: /* void $proceed(<field type>)
258: * the return type is not the field type but void.
259: */
260: static class ProceedForWrite implements ProceedHandler {
261: CtClass fieldType;
262: int opcode;
263: int targetVar, index;
264:
265: ProceedForWrite(CtClass type, int op, int i, int var) {
266: fieldType = type;
267: targetVar = var;
268: opcode = op;
269: index = i;
270: }
271:
272: public void doit(JvstCodeGen gen, Bytecode bytecode,
273: ASTList args) throws CompileError {
274: if (gen.atMethodArgsLength(args) != 1)
275: throw new CompileError(Javac.proceedName
276: + "() cannot take more than one parameter "
277: + "for field writing");
278:
279: int stack;
280: if (isStatic(opcode))
281: stack = 0;
282: else {
283: stack = -1;
284: bytecode.addAload(targetVar);
285: }
286:
287: gen.atMethodArgs(args, new int[1], new int[1],
288: new String[1]);
289: gen.doNumCast(fieldType);
290: if (fieldType instanceof CtPrimitiveType)
291: stack -= ((CtPrimitiveType) fieldType).getDataSize();
292: else
293: --stack;
294:
295: bytecode.add(opcode);
296: bytecode.addIndex(index);
297: bytecode.growStack(stack);
298: gen.setType(CtClass.voidType);
299: gen.addNullIfVoid();
300: }
301: }
302: }
|