com.feilong.lib.javassist.expr.FieldAccess Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package com.feilong.lib.javassist.expr;
import com.feilong.lib.javassist.CannotCompileException;
import com.feilong.lib.javassist.ClassPool;
import com.feilong.lib.javassist.CtBehavior;
import com.feilong.lib.javassist.CtClass;
import com.feilong.lib.javassist.CtField;
import com.feilong.lib.javassist.CtPrimitiveType;
import com.feilong.lib.javassist.NotFoundException;
import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.Bytecode;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.Descriptor;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;
import com.feilong.lib.javassist.compiler.CompileError;
import com.feilong.lib.javassist.compiler.Javac;
import com.feilong.lib.javassist.compiler.JvstCodeGen;
import com.feilong.lib.javassist.compiler.JvstTypeChecker;
import com.feilong.lib.javassist.compiler.ProceedHandler;
import com.feilong.lib.javassist.compiler.ast.ASTList;
/**
* Expression for accessing a field.
*/
public class FieldAccess extends Expr{
int opcode;
protected FieldAccess(int pos, CodeIterator i, CtClass declaring, MethodInfo m, int op){
super(pos, i, declaring, m);
opcode = op;
}
/**
* Returns the method or constructor containing the field-access
* expression represented by this object.
*/
@Override
public CtBehavior where(){
return super.where();
}
/**
* Returns the line number of the source line containing the
* field access.
*
* @return -1 if this information is not available.
*/
@Override
public int getLineNumber(){
return super.getLineNumber();
}
/**
* Returns the source file containing the field access.
*
* @return null if this information is not available.
*/
@Override
public String getFileName(){
return super.getFileName();
}
/**
* Returns true if the field is static.
*/
public boolean isStatic(){
return isStatic(opcode);
}
static boolean isStatic(int c){
return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC;
}
/**
* Returns true if the field is read.
*/
public boolean isReader(){
return opcode == Opcode.GETFIELD || opcode == Opcode.GETSTATIC;
}
/**
* Returns true if the field is written in.
*/
public boolean isWriter(){
return opcode == Opcode.PUTFIELD || opcode == Opcode.PUTSTATIC;
}
/**
* Returns the class in which the field is declared.
*/
private CtClass getCtClass() throws NotFoundException{
return thisClass.getClassPool().get(getClassName());
}
/**
* Returns the name of the class in which the field is declared.
*/
public String getClassName(){
int index = iterator.u16bitAt(currentPos + 1);
return getConstPool().getFieldrefClassName(index);
}
/**
* Returns the name of the field.
*/
public String getFieldName(){
int index = iterator.u16bitAt(currentPos + 1);
return getConstPool().getFieldrefName(index);
}
/**
* Returns the field accessed by this expression.
*/
public CtField getField() throws NotFoundException{
CtClass cc = getCtClass();
int index = iterator.u16bitAt(currentPos + 1);
ConstPool cp = getConstPool();
return cc.getField(cp.getFieldrefName(index), cp.getFieldrefType(index));
}
/**
* Returns the list of exceptions that the expression may throw.
* This list includes both the exceptions that the try-catch statements
* including the expression can catch and the exceptions that
* the throws declaration allows the method to throw.
*/
@Override
public CtClass[] mayThrow(){
return super.mayThrow();
}
/**
* Returns the signature of the field type.
* The signature is represented by a character string
* called field descriptor, which is defined in the JVM specification.
*
* @see com.feilong.lib.javassist.bytecode.Descriptor#toCtClass(String, ClassPool)
* @since 3.1
*/
public String getSignature(){
int index = iterator.u16bitAt(currentPos + 1);
return getConstPool().getFieldrefType(index);
}
/**
* Replaces the method call with the bytecode derived from
* the given source text.
*
*
* $0 is available even if the called method is static.
* If the field access is writing, $_ is available but the value
* of $_ is ignored.
*
* @param statement
* a Java statement except try-catch.
*/
@Override
public void replace(String statement) throws CannotCompileException{
thisClass.getClassFile(); // to call checkModify().
ConstPool constPool = getConstPool();
int pos = currentPos;
int index = iterator.u16bitAt(pos + 1);
Javac jc = new Javac(thisClass);
CodeAttribute ca = iterator.get();
try{
CtClass[] params;
CtClass retType;
CtClass fieldType = Descriptor.toCtClass(constPool.getFieldrefType(index), thisClass.getClassPool());
boolean read = isReader();
if (read){
params = new CtClass[0];
retType = fieldType;
}else{
params = new CtClass[1];
params[0] = fieldType;
retType = CtClass.voidType;
}
int paramVar = ca.getMaxLocals();
jc.recordParams(constPool.getFieldrefClassName(index), params, true, paramVar, withinStatic());
/*
* Is $_ included in the source code?
*/
boolean included = checkResultValue(retType, statement);
if (read){
included = true;
}
int retVar = jc.recordReturnType(retType, included);
if (read){
jc.recordProceed(new ProceedForRead(retType, opcode, index, paramVar));
}else{
// because $type is not the return type...
jc.recordType(fieldType);
jc.recordProceed(new ProceedForWrite(params[0], opcode, index, paramVar));
}
Bytecode bytecode = jc.getBytecode();
storeStack(params, isStatic(), paramVar, bytecode);
jc.recordLocalVariables(ca, pos);
if (included){
if (retType == CtClass.voidType){
bytecode.addOpcode(ACONST_NULL);
bytecode.addAstore(retVar);
}else{
bytecode.addConstZero(retType);
bytecode.addStore(retVar, retType); // initialize $_
}
}
jc.compileStmnt(statement);
if (read){
bytecode.addLoad(retVar, retType);
}
replace0(pos, bytecode, 3);
}catch (CompileError e){
throw new CannotCompileException(e);
}catch (NotFoundException e){
throw new CannotCompileException(e);
}catch (BadBytecode e){
throw new CannotCompileException("broken method");
}
}
/*
* $proceed()
*/
static class ProceedForRead implements ProceedHandler{
CtClass fieldType;
int opcode;
int targetVar, index;
ProceedForRead(CtClass type, int op, int i, int var){
fieldType = type;
targetVar = var;
opcode = op;
index = i;
}
@Override
public void doit(JvstCodeGen gen,Bytecode bytecode,ASTList args) throws CompileError{
if (args != null && !gen.isParamListName(args)){
throw new CompileError(Javac.proceedName + "() cannot take a parameter for field reading");
}
int stack;
if (isStatic(opcode)){
stack = 0;
}else{
stack = -1;
bytecode.addAload(targetVar);
}
if (fieldType instanceof CtPrimitiveType){
stack += ((CtPrimitiveType) fieldType).getDataSize();
}else{
++stack;
}
bytecode.add(opcode);
bytecode.addIndex(index);
bytecode.growStack(stack);
gen.setType(fieldType);
}
@Override
public void setReturnType(JvstTypeChecker c,ASTList args) throws CompileError{
c.setType(fieldType);
}
}
/*
* void $proceed()
* the return type is not the field type but void.
*/
static class ProceedForWrite implements ProceedHandler{
CtClass fieldType;
int opcode;
int targetVar, index;
ProceedForWrite(CtClass type, int op, int i, int var){
fieldType = type;
targetVar = var;
opcode = op;
index = i;
}
@Override
public void doit(JvstCodeGen gen,Bytecode bytecode,ASTList args) throws CompileError{
if (gen.getMethodArgsLength(args) != 1){
throw new CompileError(Javac.proceedName + "() cannot take more than one parameter " + "for field writing");
}
int stack;
if (isStatic(opcode)){
stack = 0;
}else{
stack = -1;
bytecode.addAload(targetVar);
}
gen.atMethodArgs(args, new int[1], new int[1], new String[1]);
gen.doNumCast(fieldType);
if (fieldType instanceof CtPrimitiveType){
stack -= ((CtPrimitiveType) fieldType).getDataSize();
}else{
--stack;
}
bytecode.add(opcode);
bytecode.addIndex(index);
bytecode.growStack(stack);
gen.setType(CtClass.voidType);
gen.addNullIfVoid();
}
@Override
public void setReturnType(JvstTypeChecker c,ASTList args) throws CompileError{
c.atMethodArgs(args, new int[1], new int[1], new String[1]);
c.setType(CtClass.voidType);
c.addNullIfVoid();
}
}
}