aboutsummaryrefslogtreecommitdiff
path: root/src/main/javassist/compiler/MemberCodeGen.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javassist/compiler/MemberCodeGen.java')
-rw-r--r--src/main/javassist/compiler/MemberCodeGen.java1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java
new file mode 100644
index 0000000..cbff0a4
--- /dev/null
+++ b/src/main/javassist/compiler/MemberCodeGen.java
@@ -0,0 +1,1148 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2007 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.
+ *
+ * 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 javassist.compiler;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.ast.*;
+
+import java.util.ArrayList;
+
+/* Code generator methods depending on javassist.* classes.
+ */
+public class MemberCodeGen extends CodeGen {
+ protected MemberResolver resolver;
+ protected CtClass thisClass;
+ protected MethodInfo thisMethod;
+
+ protected boolean resultStatic;
+
+ public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
+ super(b);
+ resolver = new MemberResolver(cp);
+ thisClass = cc;
+ thisMethod = null;
+ }
+
+ /**
+ * Returns the major version of the class file
+ * targeted by this compilation.
+ */
+ public int getMajorVersion() {
+ ClassFile cf = thisClass.getClassFile2();
+ if (cf == null)
+ return ClassFile.MAJOR_VERSION; // JDK 1.3
+ else
+ return cf.getMajorVersion();
+ }
+
+ /**
+ * Records the currently compiled method.
+ */
+ public void setThisMethod(CtMethod m) {
+ thisMethod = m.getMethodInfo2();
+ if (typeChecker != null)
+ typeChecker.setThisMethod(thisMethod);
+ }
+
+ public CtClass getThisClass() { return thisClass; }
+
+ /**
+ * Returns the JVM-internal representation of this class name.
+ */
+ protected String getThisName() {
+ return MemberResolver.javaToJvmName(thisClass.getName());
+ }
+
+ /**
+ * Returns the JVM-internal representation of this super class name.
+ */
+ protected String getSuperName() throws CompileError {
+ return MemberResolver.javaToJvmName(
+ MemberResolver.getSuperclass(thisClass).getName());
+ }
+
+ protected void insertDefaultSuperCall() throws CompileError {
+ bytecode.addAload(0);
+ bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
+ "<init>", "()V");
+ }
+
+ static class JsrHook extends ReturnHook {
+ ArrayList jsrList;
+ CodeGen cgen;
+ int var;
+
+ JsrHook(CodeGen gen) {
+ super(gen);
+ jsrList = new ArrayList();
+ cgen = gen;
+ var = -1;
+ }
+
+ private int getVar(int size) {
+ if (var < 0) {
+ var = cgen.getMaxLocals();
+ cgen.incMaxLocals(size);
+ }
+
+ return var;
+ }
+
+ private void jsrJmp(Bytecode b) {
+ b.addOpcode(Opcode.GOTO);
+ jsrList.add(new int[] {b.currentPc(), var});
+ b.addIndex(0);
+ }
+
+ protected boolean doit(Bytecode b, int opcode) {
+ switch (opcode) {
+ case Opcode.RETURN :
+ jsrJmp(b);
+ break;
+ case ARETURN :
+ b.addAstore(getVar(1));
+ jsrJmp(b);
+ b.addAload(var);
+ break;
+ case IRETURN :
+ b.addIstore(getVar(1));
+ jsrJmp(b);
+ b.addIload(var);
+ break;
+ case LRETURN :
+ b.addLstore(getVar(2));
+ jsrJmp(b);
+ b.addLload(var);
+ break;
+ case DRETURN :
+ b.addDstore(getVar(2));
+ jsrJmp(b);
+ b.addDload(var);
+ break;
+ case FRETURN :
+ b.addFstore(getVar(1));
+ jsrJmp(b);
+ b.addFload(var);
+ break;
+ default :
+ throw new RuntimeException("fatal");
+ }
+
+ return false;
+ }
+ }
+
+ static class JsrHook2 extends ReturnHook {
+ int var;
+ int target;
+
+ JsrHook2(CodeGen gen, int[] retTarget) {
+ super(gen);
+ target = retTarget[0];
+ var = retTarget[1];
+ }
+
+ protected boolean doit(Bytecode b, int opcode) {
+ switch (opcode) {
+ case Opcode.RETURN :
+ break;
+ case ARETURN :
+ b.addAstore(var);
+ break;
+ case IRETURN :
+ b.addIstore(var);
+ break;
+ case LRETURN :
+ b.addLstore(var);
+ break;
+ case DRETURN :
+ b.addDstore(var);
+ break;
+ case FRETURN :
+ b.addFstore(var);
+ break;
+ default :
+ throw new RuntimeException("fatal");
+ }
+
+ b.addOpcode(Opcode.GOTO);
+ b.addIndex(target - b.currentPc() + 3);
+ return true;
+ }
+ }
+
+ protected void atTryStmnt(Stmnt st) throws CompileError {
+ Bytecode bc = bytecode;
+ Stmnt body = (Stmnt)st.getLeft();
+ if (body == null)
+ return;
+
+ ASTList catchList = (ASTList)st.getRight().getLeft();
+ Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
+ ArrayList gotoList = new ArrayList();
+
+ JsrHook jsrHook = null;
+ if (finallyBlock != null)
+ jsrHook = new JsrHook(this);
+
+ int start = bc.currentPc();
+ body.accept(this);
+ int end = bc.currentPc();
+ if (start == end)
+ throw new CompileError("empty try block");
+
+ boolean tryNotReturn = !hasReturned;
+ if (tryNotReturn) {
+ bc.addOpcode(Opcode.GOTO);
+ gotoList.add(new Integer(bc.currentPc()));
+ bc.addIndex(0); // correct later
+ }
+
+ int var = getMaxLocals();
+ incMaxLocals(1);
+ while (catchList != null) {
+ // catch clause
+ Pair p = (Pair)catchList.head();
+ catchList = catchList.tail();
+ Declarator decl = (Declarator)p.getLeft();
+ Stmnt block = (Stmnt)p.getRight();
+
+ decl.setLocalVar(var);
+
+ CtClass type = resolver.lookupClassByJvmName(decl.getClassName());
+ decl.setClassName(MemberResolver.javaToJvmName(type.getName()));
+ bc.addExceptionHandler(start, end, bc.currentPc(), type);
+ bc.growStack(1);
+ bc.addAstore(var);
+ hasReturned = false;
+ if (block != null)
+ block.accept(this);
+
+ if (!hasReturned) {
+ bc.addOpcode(Opcode.GOTO);
+ gotoList.add(new Integer(bc.currentPc()));
+ bc.addIndex(0); // correct later
+ tryNotReturn = true;
+ }
+ }
+
+ if (finallyBlock != null) {
+ jsrHook.remove(this);
+ // catch (any) clause
+ int pcAnyCatch = bc.currentPc();
+ bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
+ bc.growStack(1);
+ bc.addAstore(var);
+ hasReturned = false;
+ finallyBlock.accept(this);
+ if (!hasReturned) {
+ bc.addAload(var);
+ bc.addOpcode(ATHROW);
+ }
+
+ addFinally(jsrHook.jsrList, finallyBlock);
+ }
+
+ int pcEnd = bc.currentPc();
+ patchGoto(gotoList, pcEnd);
+ hasReturned = !tryNotReturn;
+ if (finallyBlock != null) {
+ if (tryNotReturn)
+ finallyBlock.accept(this);
+ }
+ }
+
+ /**
+ * Adds a finally clause for earch return statement.
+ */
+ private void addFinally(ArrayList returnList, Stmnt finallyBlock)
+ throws CompileError
+ {
+ Bytecode bc = bytecode;
+ int n = returnList.size();
+ for (int i = 0; i < n; ++i) {
+ final int[] ret = (int[])returnList.get(i);
+ int pc = ret[0];
+ bc.write16bit(pc, bc.currentPc() - pc + 1);
+ ReturnHook hook = new JsrHook2(this, ret);
+ finallyBlock.accept(this);
+ hook.remove(this);
+ if (!hasReturned) {
+ bc.addOpcode(Opcode.GOTO);
+ bc.addIndex(pc + 3 - bc.currentPc());
+ }
+ }
+ }
+
+ public void atNewExpr(NewExpr expr) throws CompileError {
+ if (expr.isArray())
+ atNewArrayExpr(expr);
+ else {
+ CtClass clazz = resolver.lookupClassByName(expr.getClassName());
+ String cname = clazz.getName();
+ ASTList args = expr.getArguments();
+ bytecode.addNew(cname);
+ bytecode.addOpcode(DUP);
+
+ atMethodCallCore(clazz, MethodInfo.nameInit, args,
+ false, true, -1, null);
+
+ exprType = CLASS;
+ arrayDim = 0;
+ className = MemberResolver.javaToJvmName(cname);
+ }
+ }
+
+ public void atNewArrayExpr(NewExpr expr) throws CompileError {
+ int type = expr.getArrayType();
+ ASTList size = expr.getArraySize();
+ ASTList classname = expr.getClassName();
+ ArrayInit init = expr.getInitializer();
+ if (size.length() > 1) {
+ if (init != null)
+ throw new CompileError(
+ "sorry, multi-dimensional array initializer " +
+ "for new is not supported");
+
+ atMultiNewArray(type, classname, size);
+ return;
+ }
+
+ ASTree sizeExpr = size.head();
+ atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init);
+ }
+
+ private void atNewArrayExpr2(int type, ASTree sizeExpr,
+ String jvmClassname, ArrayInit init) throws CompileError {
+ if (init == null)
+ if (sizeExpr == null)
+ throw new CompileError("no array size");
+ else
+ sizeExpr.accept(this);
+ else
+ if (sizeExpr == null) {
+ int s = init.length();
+ bytecode.addIconst(s);
+ }
+ else
+ throw new CompileError("unnecessary array size specified for new");
+
+ String elementClass;
+ if (type == CLASS) {
+ elementClass = resolveClassName(jvmClassname);
+ bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass));
+ }
+ else {
+ elementClass = null;
+ int atype = 0;
+ switch (type) {
+ case BOOLEAN :
+ atype = T_BOOLEAN;
+ break;
+ case CHAR :
+ atype = T_CHAR;
+ break;
+ case FLOAT :
+ atype = T_FLOAT;
+ break;
+ case DOUBLE :
+ atype = T_DOUBLE;
+ break;
+ case BYTE :
+ atype = T_BYTE;
+ break;
+ case SHORT :
+ atype = T_SHORT;
+ break;
+ case INT :
+ atype = T_INT;
+ break;
+ case LONG :
+ atype = T_LONG;
+ break;
+ default :
+ badNewExpr();
+ break;
+ }
+
+ bytecode.addOpcode(NEWARRAY);
+ bytecode.add(atype);
+ }
+
+ if (init != null) {
+ int s = init.length();
+ ASTList list = init;
+ for (int i = 0; i < s; i++) {
+ bytecode.addOpcode(DUP);
+ bytecode.addIconst(i);
+ list.head().accept(this);
+ if (!isRefType(type))
+ atNumCastExpr(exprType, type);
+
+ bytecode.addOpcode(getArrayWriteOp(type, 0));
+ list = list.tail();
+ }
+ }
+
+ exprType = type;
+ arrayDim = 1;
+ className = elementClass;
+ }
+
+ private static void badNewExpr() throws CompileError {
+ throw new CompileError("bad new expression");
+ }
+
+ protected void atArrayVariableAssign(ArrayInit init, int varType,
+ int varArray, String varClass) throws CompileError {
+ atNewArrayExpr2(varType, null, varClass, init);
+ }
+
+ public void atArrayInit(ArrayInit init) throws CompileError {
+ throw new CompileError("array initializer is not supported");
+ }
+
+ protected void atMultiNewArray(int type, ASTList classname, ASTList size)
+ throws CompileError
+ {
+ int count, dim;
+ dim = size.length();
+ for (count = 0; size != null; size = size.tail()) {
+ ASTree s = size.head();
+ if (s == null)
+ break; // int[][][] a = new int[3][4][];
+
+ ++count;
+ s.accept(this);
+ if (exprType != INT)
+ throw new CompileError("bad type for array size");
+ }
+
+ String desc;
+ exprType = type;
+ arrayDim = dim;
+ if (type == CLASS) {
+ className = resolveClassName(classname);
+ desc = toJvmArrayName(className, dim);
+ }
+ else
+ desc = toJvmTypeName(type, dim);
+
+ bytecode.addMultiNewarray(desc, count);
+ }
+
+ public void atCallExpr(CallExpr expr) throws CompileError {
+ String mname = null;
+ CtClass targetClass = null;
+ ASTree method = expr.oprand1();
+ ASTList args = (ASTList)expr.oprand2();
+ boolean isStatic = false;
+ boolean isSpecial = false;
+ int aload0pos = -1;
+
+ MemberResolver.Method cached = expr.getMethod();
+ if (method instanceof Member) {
+ mname = ((Member)method).get();
+ targetClass = thisClass;
+ if (inStaticMethod || (cached != null && cached.isStatic()))
+ isStatic = true; // should be static
+ else {
+ aload0pos = bytecode.currentPc();
+ bytecode.addAload(0); // this
+ }
+ }
+ else if (method instanceof Keyword) { // constructor
+ isSpecial = true;
+ mname = MethodInfo.nameInit; // <init>
+ targetClass = thisClass;
+ if (inStaticMethod)
+ throw new CompileError("a constructor cannot be static");
+ else
+ bytecode.addAload(0); // this
+
+ if (((Keyword)method).get() == SUPER)
+ targetClass = MemberResolver.getSuperclass(targetClass);
+ }
+ else if (method instanceof Expr) {
+ Expr e = (Expr)method;
+ mname = ((Symbol)e.oprand2()).get();
+ int op = e.getOperator();
+ if (op == MEMBER) { // static method
+ targetClass
+ = resolver.lookupClass(((Symbol)e.oprand1()).get(), false);
+ isStatic = true;
+ }
+ else if (op == '.') {
+ ASTree target = e.oprand1();
+ if (target instanceof Keyword)
+ if (((Keyword)target).get() == SUPER)
+ isSpecial = true;
+
+ try {
+ target.accept(this);
+ }
+ catch (NoFieldException nfe) {
+ if (nfe.getExpr() != target)
+ throw nfe;
+
+ // it should be a static method.
+ exprType = CLASS;
+ arrayDim = 0;
+ className = nfe.getField(); // JVM-internal
+ resolver.recordPackage(className);
+ isStatic = true;
+ }
+
+ if (arrayDim > 0)
+ targetClass = resolver.lookupClass(javaLangObject, true);
+ else if (exprType == CLASS /* && arrayDim == 0 */)
+ targetClass = resolver.lookupClassByJvmName(className);
+ else
+ badMethod();
+ }
+ else
+ badMethod();
+ }
+ else
+ fatal();
+
+ atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
+ aload0pos, cached);
+ }
+
+ private static void badMethod() throws CompileError {
+ throw new CompileError("bad method");
+ }
+
+ /*
+ * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
+ *
+ * @param targetClass the class at which method lookup starts.
+ * @param found not null if the method look has been already done.
+ */
+ public void atMethodCallCore(CtClass targetClass, String mname,
+ ASTList args, boolean isStatic, boolean isSpecial,
+ int aload0pos, MemberResolver.Method found)
+ throws CompileError
+ {
+ int nargs = getMethodArgsLength(args);
+ int[] types = new int[nargs];
+ int[] dims = new int[nargs];
+ String[] cnames = new String[nargs];
+
+ if (!isStatic && found != null && found.isStatic()) {
+ bytecode.addOpcode(POP);
+ isStatic = true;
+ }
+
+ int stack = bytecode.getStackDepth();
+
+ // generate code for evaluating arguments.
+ atMethodArgs(args, types, dims, cnames);
+
+ // used by invokeinterface
+ int count = bytecode.getStackDepth() - stack + 1;
+
+ if (found == null)
+ found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
+ mname, types, dims, cnames);
+
+ if (found == null) {
+ String msg;
+ if (mname.equals(MethodInfo.nameInit))
+ msg = "constructor not found";
+ else
+ msg = "Method " + mname + " not found in "
+ + targetClass.getName();
+
+ throw new CompileError(msg);
+ }
+
+ atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
+ aload0pos, count, found);
+ }
+
+ private void atMethodCallCore2(CtClass targetClass, String mname,
+ boolean isStatic, boolean isSpecial,
+ int aload0pos, int count,
+ MemberResolver.Method found)
+ throws CompileError
+ {
+ CtClass declClass = found.declaring;
+ MethodInfo minfo = found.info;
+ String desc = minfo.getDescriptor();
+ int acc = minfo.getAccessFlags();
+
+ if (mname.equals(MethodInfo.nameInit)) {
+ isSpecial = true;
+ if (declClass != targetClass)
+ throw new CompileError("no such constructor");
+
+ if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
+ desc = getAccessibleConstructor(desc, declClass, minfo);
+ bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
+ }
+ }
+ else if (AccessFlag.isPrivate(acc))
+ if (declClass == thisClass)
+ isSpecial = true;
+ else {
+ isSpecial = false;
+ isStatic = true;
+ String origDesc = desc;
+ if ((acc & AccessFlag.STATIC) == 0)
+ desc = Descriptor.insertParameter(declClass.getName(),
+ origDesc);
+
+ acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
+ mname = getAccessiblePrivate(mname, origDesc, desc,
+ minfo, declClass);
+ }
+
+ boolean popTarget = false;
+ if ((acc & AccessFlag.STATIC) != 0) {
+ if (!isStatic) {
+ /* this method is static but the target object is
+ on stack. It must be popped out. If aload0pos >= 0,
+ then the target object was pushed by aload_0. It is
+ overwritten by NOP.
+ */
+ isStatic = true;
+ if (aload0pos >= 0)
+ bytecode.write(aload0pos, NOP);
+ else
+ popTarget = true;
+ }
+
+ bytecode.addInvokestatic(declClass, mname, desc);
+ }
+ else if (isSpecial) // if (isSpecial && notStatic(acc))
+ bytecode.addInvokespecial(declClass, mname, desc);
+ else {
+ if (!Modifier.isPublic(declClass.getModifiers())
+ || declClass.isInterface() != targetClass.isInterface())
+ declClass = targetClass;
+
+ if (declClass.isInterface())
+ bytecode.addInvokeinterface(declClass, mname, desc, count);
+ else
+ if (isStatic)
+ throw new CompileError(mname + " is not static");
+ else
+ bytecode.addInvokevirtual(declClass, mname, desc);
+ }
+
+ setReturnType(desc, isStatic, popTarget);
+ }
+
+ /*
+ * Finds (or adds if necessary) a hidden accessor if the method
+ * is in an enclosing class.
+ *
+ * @param desc the descriptor of the method.
+ * @param declClass the class declaring the method.
+ */
+ protected String getAccessiblePrivate(String methodName, String desc,
+ String newDesc, MethodInfo minfo,
+ CtClass declClass)
+ throws CompileError
+ {
+ if (isEnclosing(declClass, thisClass)) {
+ AccessorMaker maker = declClass.getAccessorMaker();
+ if (maker != null)
+ return maker.getMethodAccessor(methodName, desc, newDesc,
+ minfo);
+ }
+
+ throw new CompileError("Method " + methodName
+ + " is private");
+ }
+
+ /*
+ * Finds (or adds if necessary) a hidden constructor if the given
+ * constructor is in an enclosing class.
+ *
+ * @param desc the descriptor of the constructor.
+ * @param declClass the class declaring the constructor.
+ * @param minfo the method info of the constructor.
+ * @return the descriptor of the hidden constructor.
+ */
+ protected String getAccessibleConstructor(String desc, CtClass declClass,
+ MethodInfo minfo)
+ throws CompileError
+ {
+ if (isEnclosing(declClass, thisClass)) {
+ AccessorMaker maker = declClass.getAccessorMaker();
+ if (maker != null)
+ return maker.getConstructor(declClass, desc, minfo);
+ }
+
+ throw new CompileError("the called constructor is private in "
+ + declClass.getName());
+ }
+
+ private boolean isEnclosing(CtClass outer, CtClass inner) {
+ try {
+ while (inner != null) {
+ inner = inner.getDeclaringClass();
+ if (inner == outer)
+ return true;
+ }
+ }
+ catch (NotFoundException e) {}
+ return false;
+ }
+
+ public int getMethodArgsLength(ASTList args) {
+ return ASTList.length(args);
+ }
+
+ public void atMethodArgs(ASTList args, int[] types, int[] dims,
+ String[] cnames) throws CompileError {
+ int i = 0;
+ while (args != null) {
+ ASTree a = args.head();
+ a.accept(this);
+ types[i] = exprType;
+ dims[i] = arrayDim;
+ cnames[i] = className;
+ ++i;
+ args = args.tail();
+ }
+ }
+
+ void setReturnType(String desc, boolean isStatic, boolean popTarget)
+ throws CompileError
+ {
+ int i = desc.indexOf(')');
+ if (i < 0)
+ badMethod();
+
+ char c = desc.charAt(++i);
+ int dim = 0;
+ while (c == '[') {
+ ++dim;
+ c = desc.charAt(++i);
+ }
+
+ arrayDim = dim;
+ if (c == 'L') {
+ int j = desc.indexOf(';', i + 1);
+ if (j < 0)
+ badMethod();
+
+ exprType = CLASS;
+ className = desc.substring(i + 1, j);
+ }
+ else {
+ exprType = MemberResolver.descToType(c);
+ className = null;
+ }
+
+ int etype = exprType;
+ if (isStatic) {
+ if (popTarget) {
+ if (is2word(etype, dim)) {
+ bytecode.addOpcode(DUP2_X1);
+ bytecode.addOpcode(POP2);
+ bytecode.addOpcode(POP);
+ }
+ else if (etype == VOID)
+ bytecode.addOpcode(POP);
+ else {
+ bytecode.addOpcode(SWAP);
+ bytecode.addOpcode(POP);
+ }
+ }
+ }
+ }
+
+ protected void atFieldAssign(Expr expr, int op, ASTree left,
+ ASTree right, boolean doDup) throws CompileError
+ {
+ CtField f = fieldAccess(left, false);
+ boolean is_static = resultStatic;
+ if (op != '=' && !is_static)
+ bytecode.addOpcode(DUP);
+
+ int fi;
+ if (op == '=') {
+ FieldInfo finfo = f.getFieldInfo2();
+ setFieldType(finfo);
+ AccessorMaker maker = isAccessibleField(f, finfo);
+ if (maker == null)
+ fi = addFieldrefInfo(f, finfo);
+ else
+ fi = 0;
+ }
+ else
+ fi = atFieldRead(f, is_static);
+
+ int fType = exprType;
+ int fDim = arrayDim;
+ String cname = className;
+
+ atAssignCore(expr, op, right, fType, fDim, cname);
+
+ boolean is2w = is2word(fType, fDim);
+ if (doDup) {
+ int dup_code;
+ if (is_static)
+ dup_code = (is2w ? DUP2 : DUP);
+ else
+ dup_code = (is2w ? DUP2_X1 : DUP_X1);
+
+ bytecode.addOpcode(dup_code);
+ }
+
+ atFieldAssignCore(f, is_static, fi, is2w);
+
+ exprType = fType;
+ arrayDim = fDim;
+ className = cname;
+ }
+
+ /* If fi == 0, the field must be a private field in an enclosing class.
+ */
+ private void atFieldAssignCore(CtField f, boolean is_static, int fi,
+ boolean is2byte) throws CompileError {
+ if (fi != 0) {
+ if (is_static) {
+ bytecode.add(PUTSTATIC);
+ bytecode.growStack(is2byte ? -2 : -1);
+ }
+ else {
+ bytecode.add(PUTFIELD);
+ bytecode.growStack(is2byte ? -3 : -2);
+ }
+
+ bytecode.addIndex(fi);
+ }
+ else {
+ CtClass declClass = f.getDeclaringClass();
+ AccessorMaker maker = declClass.getAccessorMaker();
+ // make should be non null.
+ FieldInfo finfo = f.getFieldInfo2();
+ MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
+ bytecode.addInvokestatic(declClass, minfo.getName(),
+ minfo.getDescriptor());
+ }
+ }
+
+ /* overwritten in JvstCodeGen.
+ */
+ public void atMember(Member mem) throws CompileError {
+ atFieldRead(mem);
+ }
+
+ protected void atFieldRead(ASTree expr) throws CompileError
+ {
+ CtField f = fieldAccess(expr, true);
+ if (f == null) {
+ atArrayLength(expr);
+ return;
+ }
+
+ boolean is_static = resultStatic;
+ ASTree cexpr = TypeChecker.getConstantFieldValue(f);
+ if (cexpr == null)
+ atFieldRead(f, is_static);
+ else {
+ cexpr.accept(this);
+ setFieldType(f.getFieldInfo2());
+ }
+ }
+
+ private void atArrayLength(ASTree expr) throws CompileError {
+ if (arrayDim == 0)
+ throw new CompileError(".length applied to a non array");
+
+ bytecode.addOpcode(ARRAYLENGTH);
+ exprType = INT;
+ arrayDim = 0;
+ }
+
+ /**
+ * Generates bytecode for reading a field value.
+ * It returns a fieldref_info index or zero if the field is a private
+ * one declared in an enclosing class.
+ */
+ private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
+ FieldInfo finfo = f.getFieldInfo2();
+ boolean is2byte = setFieldType(finfo);
+ AccessorMaker maker = isAccessibleField(f, finfo);
+ if (maker != null) {
+ MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
+ bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
+ minfo.getDescriptor());
+ return 0;
+ }
+ else {
+ int fi = addFieldrefInfo(f, finfo);
+ if (isStatic) {
+ bytecode.add(GETSTATIC);
+ bytecode.growStack(is2byte ? 2 : 1);
+ }
+ else {
+ bytecode.add(GETFIELD);
+ bytecode.growStack(is2byte ? 1 : 0);
+ }
+
+ bytecode.addIndex(fi);
+ return fi;
+ }
+ }
+
+ /**
+ * Returns null if the field is accessible. Otherwise, it throws
+ * an exception or it returns AccessorMaker if the field is a private
+ * one declared in an enclosing class.
+ */
+ private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
+ throws CompileError
+ {
+ if (AccessFlag.isPrivate(finfo.getAccessFlags())
+ && f.getDeclaringClass() != thisClass) {
+ CtClass declClass = f.getDeclaringClass();
+ if (isEnclosing(declClass, thisClass)) {
+ AccessorMaker maker = declClass.getAccessorMaker();
+ if (maker != null)
+ return maker;
+ else
+ throw new CompileError("fatal error. bug?");
+ }
+ else
+ throw new CompileError("Field " + f.getName() + " in "
+ + declClass.getName() + " is private.");
+ }
+
+ return null; // accessible field
+ }
+
+ /**
+ * Sets exprType, arrayDim, and className.
+ *
+ * @return true if the field type is long or double.
+ */
+ private boolean setFieldType(FieldInfo finfo) throws CompileError {
+ String type = finfo.getDescriptor();
+
+ int i = 0;
+ int dim = 0;
+ char c = type.charAt(i);
+ while (c == '[') {
+ ++dim;
+ c = type.charAt(++i);
+ }
+
+ arrayDim = dim;
+ exprType = MemberResolver.descToType(c);
+
+ if (c == 'L')
+ className = type.substring(i + 1, type.indexOf(';', i + 1));
+ else
+ className = null;
+
+ boolean is2byte = (c == 'J' || c == 'D');
+ return is2byte;
+ }
+
+ private int addFieldrefInfo(CtField f, FieldInfo finfo) {
+ ConstPool cp = bytecode.getConstPool();
+ String cname = f.getDeclaringClass().getName();
+ int ci = cp.addClassInfo(cname);
+ String name = finfo.getName();
+ String type = finfo.getDescriptor();
+ return cp.addFieldrefInfo(ci, name, type);
+ }
+
+ protected void atClassObject2(String cname) throws CompileError {
+ if (getMajorVersion() < ClassFile.JAVA_5)
+ super.atClassObject2(cname);
+ else
+ bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
+ }
+
+ protected void atFieldPlusPlus(int token, boolean isPost,
+ ASTree oprand, Expr expr, boolean doDup)
+ throws CompileError
+ {
+ CtField f = fieldAccess(oprand, false);
+ boolean is_static = resultStatic;
+ if (!is_static)
+ bytecode.addOpcode(DUP);
+
+ int fi = atFieldRead(f, is_static);
+ int t = exprType;
+ boolean is2w = is2word(t, arrayDim);
+
+ int dup_code;
+ if (is_static)
+ dup_code = (is2w ? DUP2 : DUP);
+ else
+ dup_code = (is2w ? DUP2_X1 : DUP_X1);
+
+ atPlusPlusCore(dup_code, doDup, token, isPost, expr);
+ atFieldAssignCore(f, is_static, fi, is2w);
+ }
+
+ /* This method also returns a value in resultStatic.
+ *
+ * @param acceptLength true if array length is acceptable
+ */
+ protected CtField fieldAccess(ASTree expr, boolean acceptLength)
+ throws CompileError
+ {
+ if (expr instanceof Member) {
+ String name = ((Member)expr).get();
+ CtField f = null;
+ try {
+ f = thisClass.getField(name);
+ }
+ catch (NotFoundException e) {
+ // EXPR might be part of a static member access?
+ throw new NoFieldException(name, expr);
+ }
+
+ boolean is_static = Modifier.isStatic(f.getModifiers());
+ if (!is_static)
+ if (inStaticMethod)
+ throw new CompileError(
+ "not available in a static method: " + name);
+ else
+ bytecode.addAload(0); // this
+
+ resultStatic = is_static;
+ return f;
+ }
+ else if (expr instanceof Expr) {
+ Expr e = (Expr)expr;
+ int op = e.getOperator();
+ if (op == MEMBER) {
+ /* static member by # (extension by Javassist)
+ * For example, if int.class is parsed, the resulting tree
+ * is (# "java.lang.Integer" "TYPE").
+ */
+ CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(),
+ (Symbol)e.oprand2());
+ resultStatic = true;
+ return f;
+ }
+ else if (op == '.') {
+ CtField f = null;
+ try {
+ e.oprand1().accept(this);
+ /* Don't call lookupFieldByJvmName2().
+ * The left operand of . is not a class name but
+ * a normal expression.
+ */
+ if (exprType == CLASS && arrayDim == 0)
+ f = resolver.lookupFieldByJvmName(className,
+ (Symbol)e.oprand2());
+ else if (acceptLength && arrayDim > 0
+ && ((Symbol)e.oprand2()).get().equals("length"))
+ return null; // expr is an array length.
+ else
+ badLvalue();
+
+ boolean is_static = Modifier.isStatic(f.getModifiers());
+ if (is_static)
+ bytecode.addOpcode(POP);
+
+ resultStatic = is_static;
+ return f;
+ }
+ catch (NoFieldException nfe) {
+ if (nfe.getExpr() != e.oprand1())
+ throw nfe;
+
+ /* EXPR should be a static field.
+ * If EXPR might be part of a qualified class name,
+ * lookupFieldByJvmName2() throws NoFieldException.
+ */
+ Symbol fname = (Symbol)e.oprand2();
+ String cname = nfe.getField();
+ f = resolver.lookupFieldByJvmName2(cname, fname, expr);
+ resolver.recordPackage(cname);
+ resultStatic = true;
+ return f;
+ }
+ }
+ else
+ badLvalue();
+ }
+ else
+ badLvalue();
+
+ resultStatic = false;
+ return null; // never reach
+ }
+
+ private static void badLvalue() throws CompileError {
+ throw new CompileError("bad l-value");
+ }
+
+ public CtClass[] makeParamList(MethodDecl md) throws CompileError {
+ CtClass[] params;
+ ASTList plist = md.getParams();
+ if (plist == null)
+ params = new CtClass[0];
+ else {
+ int i = 0;
+ params = new CtClass[plist.length()];
+ while (plist != null) {
+ params[i++] = resolver.lookupClass((Declarator)plist.head());
+ plist = plist.tail();
+ }
+ }
+
+ return params;
+ }
+
+ public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
+ CtClass[] clist;
+ ASTList list = md.getThrows();
+ if (list == null)
+ return null;
+ else {
+ int i = 0;
+ clist = new CtClass[list.length()];
+ while (list != null) {
+ clist[i++] = resolver.lookupClassByName((ASTList)list.head());
+ list = list.tail();
+ }
+
+ return clist;
+ }
+ }
+
+ /* Converts a class name into a JVM-internal representation.
+ *
+ * It may also expand a simple class name to java.lang.*.
+ * For example, this converts Object into java/lang/Object.
+ */
+ protected String resolveClassName(ASTList name) throws CompileError {
+ return resolver.resolveClassName(name);
+ }
+
+ /* Expands a simple class name to java.lang.*.
+ * For example, this converts Object into java/lang/Object.
+ */
+ protected String resolveClassName(String jvmName) throws CompileError {
+ return resolver.resolveJvmClassName(jvmName);
+ }
+}