scouter.javassist.expr.FieldAccess Maven / Gradle / Ivy
/*
* 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 scouter.javassist.expr;
import scouter.javassist.CannotCompileException;
import scouter.javassist.ClassPool;
import scouter.javassist.CtBehavior;
import scouter.javassist.CtClass;
import scouter.javassist.CtField;
import scouter.javassist.CtPrimitiveType;
import scouter.javassist.NotFoundException;
import scouter.javassist.bytecode.BadBytecode;
import scouter.javassist.bytecode.Bytecode;
import scouter.javassist.bytecode.CodeAttribute;
import scouter.javassist.bytecode.CodeIterator;
import scouter.javassist.bytecode.ConstPool;
import scouter.javassist.bytecode.Descriptor;
import scouter.javassist.bytecode.MethodInfo;
import scouter.javassist.bytecode.Opcode;
import scouter.javassist.compiler.CompileError;
import scouter.javassist.compiler.Javac;
import scouter.javassist.compiler.JvstCodeGen;
import scouter.javassist.compiler.JvstTypeChecker;
import scouter.javassist.compiler.ProceedHandler;
import scouter.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 scouter.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();
}
}
}