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