1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999- 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 * or the Apache License Version 2.0. 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 */ 16 17 package javassist.expr; 18 19 import javassist.CannotCompileException; 20 import javassist.ClassPool; 21 import javassist.CtBehavior; 22 import javassist.CtClass; 23 import javassist.CtField; 24 import javassist.CtPrimitiveType; 25 import javassist.NotFoundException; 26 import javassist.bytecode.BadBytecode; 27 import javassist.bytecode.Bytecode; 28 import javassist.bytecode.CodeAttribute; 29 import javassist.bytecode.CodeIterator; 30 import javassist.bytecode.ConstPool; 31 import javassist.bytecode.Descriptor; 32 import javassist.bytecode.MethodInfo; 33 import javassist.bytecode.Opcode; 34 import javassist.compiler.CompileError; 35 import javassist.compiler.Javac; 36 import javassist.compiler.JvstCodeGen; 37 import javassist.compiler.JvstTypeChecker; 38 import javassist.compiler.ProceedHandler; 39 import javassist.compiler.ast.ASTList; 40 41 /** 42 * Expression for accessing a field. 43 */ 44 public class FieldAccess extends Expr { 45 int opcode; 46 FieldAccess(int pos, CodeIterator i, CtClass declaring, MethodInfo m, int op)47 protected FieldAccess(int pos, CodeIterator i, CtClass declaring, 48 MethodInfo m, int op) { 49 super(pos, i, declaring, m); 50 opcode = op; 51 } 52 53 /** 54 * Returns the method or constructor containing the field-access 55 * expression represented by this object. 56 */ 57 @Override where()58 public CtBehavior where() { return super.where(); } 59 60 /** 61 * Returns the line number of the source line containing the 62 * field access. 63 * 64 * @return -1 if this information is not available. 65 */ 66 @Override getLineNumber()67 public int getLineNumber() { 68 return super.getLineNumber(); 69 } 70 71 /** 72 * Returns the source file containing the field access. 73 * 74 * @return null if this information is not available. 75 */ 76 @Override getFileName()77 public String getFileName() { 78 return super.getFileName(); 79 } 80 81 /** 82 * Returns true if the field is static. 83 */ isStatic()84 public boolean isStatic() { 85 return isStatic(opcode); 86 } 87 isStatic(int c)88 static boolean isStatic(int c) { 89 return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC; 90 } 91 92 /** 93 * Returns true if the field is read. 94 */ isReader()95 public boolean isReader() { 96 return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC; 97 } 98 99 /** 100 * Returns true if the field is written in. 101 */ isWriter()102 public boolean isWriter() { 103 return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC; 104 } 105 106 /** 107 * Returns the class in which the field is declared. 108 */ getCtClass()109 private CtClass getCtClass() throws NotFoundException { 110 return thisClass.getClassPool().get(getClassName()); 111 } 112 113 /** 114 * Returns the name of the class in which the field is declared. 115 */ getClassName()116 public String getClassName() { 117 int index = iterator.u16bitAt(currentPos + 1); 118 return getConstPool().getFieldrefClassName(index); 119 } 120 121 /** 122 * Returns the name of the field. 123 */ getFieldName()124 public String getFieldName() { 125 int index = iterator.u16bitAt(currentPos + 1); 126 return getConstPool().getFieldrefName(index); 127 } 128 129 /** 130 * Returns the field accessed by this expression. 131 */ getField()132 public CtField getField() throws NotFoundException { 133 CtClass cc = getCtClass(); 134 int index = iterator.u16bitAt(currentPos + 1); 135 ConstPool cp = getConstPool(); 136 return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index)); 137 } 138 139 /** 140 * Returns the list of exceptions that the expression may throw. 141 * This list includes both the exceptions that the try-catch statements 142 * including the expression can catch and the exceptions that 143 * the throws declaration allows the method to throw. 144 */ 145 @Override mayThrow()146 public CtClass[] mayThrow() { 147 return super.mayThrow(); 148 } 149 150 /** 151 * Returns the signature of the field type. 152 * The signature is represented by a character string 153 * called field descriptor, which is defined in the JVM specification. 154 * 155 * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) 156 * @since 3.1 157 */ getSignature()158 public String getSignature() { 159 int index = iterator.u16bitAt(currentPos + 1); 160 return getConstPool().getFieldrefType(index); 161 } 162 163 /** 164 * Replaces the method call with the bytecode derived from 165 * the given source text. 166 * 167 * <p>$0 is available even if the called method is static. 168 * If the field access is writing, $_ is available but the value 169 * of $_ is ignored. 170 * 171 * @param statement a Java statement except try-catch. 172 */ 173 @Override replace(String statement)174 public void replace(String statement) throws CannotCompileException { 175 thisClass.getClassFile(); // to call checkModify(). 176 ConstPool constPool = getConstPool(); 177 int pos = currentPos; 178 int index = iterator.u16bitAt(pos + 1); 179 180 Javac jc = new Javac(thisClass); 181 CodeAttribute ca = iterator.get(); 182 try { 183 CtClass[] params; 184 CtClass retType; 185 CtClass fieldType 186 = Descriptor.toCtClass(constPool.getFieldrefType(index), 187 thisClass.getClassPool()); 188 boolean read = isReader(); 189 if (read) { 190 params = new CtClass[0]; 191 retType = fieldType; 192 } 193 else { 194 params = new CtClass[1]; 195 params[0] = fieldType; 196 retType = CtClass.voidType; 197 } 198 199 int paramVar = ca.getMaxLocals(); 200 jc.recordParams(constPool.getFieldrefClassName(index), params, 201 true, paramVar, withinStatic()); 202 203 /* Is $_ included in the source code? 204 */ 205 boolean included = checkResultValue(retType, statement); 206 if (read) 207 included = true; 208 209 int retVar = jc.recordReturnType(retType, included); 210 if (read) 211 jc.recordProceed(new ProceedForRead(retType, opcode, 212 index, paramVar)); 213 else { 214 // because $type is not the return type... 215 jc.recordType(fieldType); 216 jc.recordProceed(new ProceedForWrite(params[0], opcode, 217 index, paramVar)); 218 } 219 220 Bytecode bytecode = jc.getBytecode(); 221 storeStack(params, isStatic(), paramVar, bytecode); 222 jc.recordLocalVariables(ca, pos); 223 224 if (included) 225 if (retType == CtClass.voidType) { 226 bytecode.addOpcode(ACONST_NULL); 227 bytecode.addAstore(retVar); 228 } 229 else { 230 bytecode.addConstZero(retType); 231 bytecode.addStore(retVar, retType); // initialize $_ 232 } 233 234 jc.compileStmnt(statement); 235 if (read) 236 bytecode.addLoad(retVar, retType); 237 238 replace0(pos, bytecode, 3); 239 } 240 catch (CompileError e) { throw new CannotCompileException(e); } 241 catch (NotFoundException e) { throw new CannotCompileException(e); } 242 catch (BadBytecode e) { 243 throw new CannotCompileException("broken method"); 244 } 245 } 246 247 /* <field type> $proceed() 248 */ 249 static class ProceedForRead implements ProceedHandler { 250 CtClass fieldType; 251 int opcode; 252 int targetVar, index; 253 ProceedForRead(CtClass type, int op, int i, int var)254 ProceedForRead(CtClass type, int op, int i, int var) { 255 fieldType = type; 256 targetVar = var; 257 opcode = op; 258 index = i; 259 } 260 261 @Override doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)262 public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) 263 throws CompileError 264 { 265 if (args != null && !gen.isParamListName(args)) 266 throw new CompileError(Javac.proceedName 267 + "() cannot take a parameter for field reading"); 268 269 int stack; 270 if (isStatic(opcode)) 271 stack = 0; 272 else { 273 stack = -1; 274 bytecode.addAload(targetVar); 275 } 276 277 if (fieldType instanceof CtPrimitiveType) 278 stack += ((CtPrimitiveType)fieldType).getDataSize(); 279 else 280 ++stack; 281 282 bytecode.add(opcode); 283 bytecode.addIndex(index); 284 bytecode.growStack(stack); 285 gen.setType(fieldType); 286 } 287 288 @Override setReturnType(JvstTypeChecker c, ASTList args)289 public void setReturnType(JvstTypeChecker c, ASTList args) 290 throws CompileError 291 { 292 c.setType(fieldType); 293 } 294 } 295 296 /* void $proceed(<field type>) 297 * the return type is not the field type but void. 298 */ 299 static class ProceedForWrite implements ProceedHandler { 300 CtClass fieldType; 301 int opcode; 302 int targetVar, index; 303 ProceedForWrite(CtClass type, int op, int i, int var)304 ProceedForWrite(CtClass type, int op, int i, int var) { 305 fieldType = type; 306 targetVar = var; 307 opcode = op; 308 index = i; 309 } 310 311 @Override doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)312 public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) 313 throws CompileError 314 { 315 if (gen.getMethodArgsLength(args) != 1) 316 throw new CompileError(Javac.proceedName 317 + "() cannot take more than one parameter " 318 + "for field writing"); 319 320 int stack; 321 if (isStatic(opcode)) 322 stack = 0; 323 else { 324 stack = -1; 325 bytecode.addAload(targetVar); 326 } 327 328 gen.atMethodArgs(args, new int[1], new int[1], new String[1]); 329 gen.doNumCast(fieldType); 330 if (fieldType instanceof CtPrimitiveType) 331 stack -= ((CtPrimitiveType)fieldType).getDataSize(); 332 else 333 --stack; 334 335 bytecode.add(opcode); 336 bytecode.addIndex(index); 337 bytecode.growStack(stack); 338 gen.setType(CtClass.voidType); 339 gen.addNullIfVoid(); 340 } 341 342 @Override setReturnType(JvstTypeChecker c, ASTList args)343 public void setReturnType(JvstTypeChecker c, ASTList args) 344 throws CompileError 345 { 346 c.atMethodArgs(args, new int[1], new int[1], new String[1]); 347 c.setType(CtClass.voidType); 348 c.addNullIfVoid(); 349 } 350 } 351 } 352