/* * 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 javassist.compiler; import java.util.ArrayList; import java.util.List; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; import javassist.compiler.ast.ASTList; import javassist.compiler.ast.ASTree; import javassist.compiler.ast.ArrayInit; import javassist.compiler.ast.CallExpr; import javassist.compiler.ast.Declarator; import javassist.compiler.ast.Expr; import javassist.compiler.ast.Keyword; import javassist.compiler.ast.Member; import javassist.compiler.ast.MethodDecl; import javassist.compiler.ast.NewExpr; import javassist.compiler.ast.Pair; import javassist.compiler.ast.Stmnt; import javassist.compiler.ast.Symbol; /* 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 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. */ @Override protected String getThisName() { return MemberResolver.javaToJvmName(thisClass.getName()); } /** * Returns the JVM-internal representation of this super class name. */ @Override protected String getSuperName() throws CompileError { return MemberResolver.javaToJvmName( MemberResolver.getSuperclass(thisClass).getName()); } @Override protected void insertDefaultSuperCall() throws CompileError { bytecode.addAload(0); bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), "", "()V"); } static class JsrHook extends ReturnHook { List 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); } @Override 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]; } @Override 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; } } @Override 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(); List 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(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(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(List returnList, Stmnt finallyBlock) throws CompileError { Bytecode bc = bytecode; for (final int[] ret:returnList) { 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()); } } } @Override 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.size(); 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.size(); 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"); } @Override protected void atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass) throws CompileError { atNewArrayExpr2(varType, null, varClass, init); } @Override 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); } @Override 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; // targetClass = thisClass; if (inStaticMethod) throw new CompileError("a constructor cannot be static"); 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(); String classFollowedByDotSuper = TypeChecker.isDotSuper(target); if (classFollowedByDotSuper != null) { isSpecial = true; targetClass = MemberResolver.getSuperInterface(thisClass, classFollowedByDotSuper); if (inStaticMethod || (cached != null && cached.isStatic())) isStatic = true; // should be static else { aload0pos = bytecode.currentPc(); bytecode.addAload(0); // this } } else { 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 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; } @SuppressWarnings("unused") int stack = bytecode.getStackDepth(); // generate code for evaluating arguments. atMethodArgs(args, types, dims, cnames); 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, found); } private boolean isFromSameDeclaringClass(CtClass outer, CtClass inner) { try { while (outer != null) { if (isEnclosing(outer, inner)) return true; outer = outer.getDeclaringClass(); } } catch (NotFoundException e) {} return false; } private void atMethodCallCore2(CtClass targetClass, String mname, boolean isStatic, boolean isSpecial, int aload0pos, 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: " + targetClass.getName()); if (declClass != thisClass && AccessFlag.isPrivate(acc)) { if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_11 || !isFromSameDeclaringClass(declClass, thisClass)) { 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; if (!isFromSameDeclaringClass(declClass, thisClass)) 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(targetClass, mname, desc); else { if (!Modifier.isPublic(declClass.getModifiers()) || declClass.isInterface() != targetClass.isInterface()) declClass = targetClass; if (declClass.isInterface()) { int nargs = Descriptor.paramSize(desc) + 1; bytecode.addInvokeinterface(declClass, mname, desc, nargs); } 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); } } } } @Override 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. */ @Override public void atMember(Member mem) throws CompileError { atFieldRead(mem); } @Override 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; } 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; } 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 = dim == 0 && (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); } @Override protected void atClassObject2(String cname) throws CompileError { if (getMajorVersion() < ClassFile.JAVA_5) super.atClassObject2(cname); else bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname)); } @Override 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); 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; 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. */ @Override 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. */ @Override protected String resolveClassName(String jvmName) throws CompileError { return resolver.resolveJvmClassName(jvmName); } }