Home | History | Annotate | Download | only in expr
      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 
     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      */
     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      */
     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      */
     56     public String getFileName() {
     57         return super.getFileName();
     58     }
     59 
     60     /**
     61      * Returns true if the field is static.
     62      */
     63     public boolean isStatic() {
     64         return isStatic(opcode);
     65     }
     66 
     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      */
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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 
    229         ProceedForRead(CtClass type, int op, int i, int var) {
    230             fieldType = type;
    231             targetVar = var;
    232             opcode = op;
    233             index = i;
    234         }
    235 
    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 
    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 
    277         ProceedForWrite(CtClass type, int op, int i, int var) {
    278             fieldType = type;
    279             targetVar = var;
    280             opcode = op;
    281             index = i;
    282         }
    283 
    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 
    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