*/
public ClassFile getClassFile2() { return null; }
+ /**
+ * Undocumented method. Do not use; internal-use only.
+ */
+ public javassist.compiler.AccessorMaker getAccessorMaker() {
+ return null;
+ }
+
/**
* Returns the uniform resource locator (URL) of the class file.
*/
void checkModify() throws RuntimeException {
if (isFrozen())
- throw new RuntimeException("the class is frozen");
+ throw new RuntimeException(getName() + " class is frozen");
// isModified() must return true after this method is invoked.
}
import javassist.bytecode.*;
import javassist.compiler.Javac;
import javassist.compiler.CompileError;
+import javassist.compiler.AccessorMaker;
import javassist.expr.ExprEditor;
import java.io.InputStream;
import java.io.BufferedInputStream;
private CtConstructor classInitializerCache;
private CtMethod methodsCache;
+ private AccessorMaker accessors;
+
private FieldInitLink fieldInitializers;
private Hashtable hiddenMethods; // must be synchronous
private int uniqueNumberSeed;
classPool = cp;
wasChanged = wasFrozen = false;
classfile = null;
+ accessors = null;
fieldInitializers = null;
hiddenMethods = null;
uniqueNumberSeed = 0;
methodsCache = null;
}
+ public AccessorMaker getAccessorMaker() {
+ if (accessors == null)
+ accessors = new AccessorMaker(this);
+
+ return accessors;
+ }
+
public ClassFile getClassFile2() {
if (classfile != null)
return classfile;
stackDepth = 0;
}
+ /**
+ * Constructs a <code>Bytecode</code> object with an empty bytecode
+ * sequence. The initial values of <code>max_stack</code> and
+ * <code>max_locals</code> are zero.
+ *
+ * @param cp constant pool table.
+ * @see Bytecode#setMaxStack(int)
+ * @see Bytecode#setMaxLocals(int)
+ */
+ public Bytecode(ConstPool cp) {
+ this(cp, 0, 0);
+ }
+
/* used in add().
*/
private Bytecode() {
*
* @param newCp the constant pool table used by the new copy.
* @param classnames pairs of replaced and substituted
- * class names.
+ * class names. It can be <code>null</code>.
*/
public AttributeInfo copy(ConstPool newCp, Map classnames) {
return new ExceptionsAttribute(newCp, this, classnames);
--- /dev/null
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2004 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 java.util.HashMap;
+
+/**
+ * AccessorMaker maintains accessors to private members of an enclosing
+ * class. It is necessary for compiling a method in an inner class.
+ */
+public class AccessorMaker {
+ private CtClass clazz;
+ private int uniqueNumber;
+ private HashMap accessors;
+
+ public AccessorMaker(CtClass c) {
+ clazz = c;
+ uniqueNumber = 1;
+ accessors = new HashMap();
+ }
+
+ /**
+ * Returns the name of the method for accessing a private method.
+ *
+ * @param name the name of the private method.
+ * @param desc the descriptor of the private method.
+ * @param accDesc the descriptor of the accessor method. The first
+ * parameter type is <code>clazz</code>.
+ * If the private method is static,
+ * <code>accDesc<code> must be equal to <code>desc</code>.
+ *
+ * @param orig the method info of the private method.
+ * @return
+ */
+ public String getMethodAccessor(String name, String desc, String accDesc,
+ MethodInfo orig)
+ throws CompileError
+ {
+ String key = name + ":" + desc;
+ String accName = (String)accessors.get(key);
+ if (accName != null)
+ return accName; // already exists.
+
+ ClassFile cf = clazz.getClassFile(); // turn on the modified flag.
+ do {
+ accName = "access$" + uniqueNumber++;
+ } while (cf.getMethod(accName) != null);
+
+ try {
+ ConstPool cp = cf.getConstPool();
+ ClassPool pool = clazz.getClassPool();
+ MethodInfo minfo
+ = new MethodInfo(cp, accName, accDesc);
+ minfo.setAccessFlags(AccessFlag.STATIC);
+ minfo.addAttribute(new SyntheticAttribute(cp));
+ ExceptionsAttribute ea = orig.getExceptionsAttribute();
+ if (ea != null)
+ minfo.addAttribute(ea.copy(cp, null));
+
+ CtClass[] params = Descriptor.getParameterTypes(accDesc, pool);
+ int regno = 0;
+ Bytecode code = new Bytecode(cp);
+ for (int i = 0; i < params.length; ++i)
+ regno += code.addLoad(regno, params[i]);
+
+ code.setMaxLocals(regno);
+ if (desc == accDesc)
+ code.addInvokestatic(clazz, name, desc);
+ else
+ code.addInvokevirtual(clazz, name, desc);
+
+ code.addReturn(Descriptor.getReturnType(desc, pool));
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ cf.addMethod(minfo);
+ }
+ catch (CannotCompileException e) {
+ throw new CompileError(e);
+ }
+ catch (NotFoundException e) {
+ throw new CompileError(e);
+ }
+
+ accessors.put(key, accName);
+ return accName;
+ }
+}
package javassist.compiler;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+
public class CompileError extends Exception {
private Lex lex;
private String reason;
lex = null;
}
+ public CompileError(CannotCompileException e) {
+ this(e.getReason());
+ }
+
+ public CompileError(NotFoundException e) {
+ this("cannot find " + e.getMessage());
+ }
+
public String getMessage() {
return reason;
}
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();
throw new CompileError("no such a constructor");
}
else if ((acc & AccessFlag.PRIVATE) != 0) {
- isSpecial = true;
- String orgName = mname;
- mname = getAccessiblePrivate(mname, declClass);
- if (mname == null)
- throw new CompileError("Method " + orgName + " is private");
+ if (declClass == thisClass)
+ isSpecial = true;
+ else {
+ String orgName = mname;
+ 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);
+ if (mname == null)
+ throw new CompileError("Method " + orgName
+ + " is private");
+ }
}
boolean popTarget = false;
bytecode.addInvokestatic(declClass, mname, desc);
}
- else if (isSpecial)
+ else if (isSpecial) // if (isSpecial && notStatic(acc))
bytecode.addInvokespecial(declClass, mname, desc);
else if (declClass.isInterface())
bytecode.addInvokeinterface(declClass, mname, desc, count);
setReturnType(desc, isStatic, popTarget);
}
- protected String getAccessiblePrivate(String methodName,
- CtClass declClass) {
- if (declClass == thisClass)
- return methodName;
- else if (isEnclosing(declClass, thisClass))
- return null;
+ /*
+ * 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 null;
+ else
+ return maker.getMethodAccessor(methodName, desc, newDesc,
+ minfo);
+ }
else
return null; // cannot access this private method.
}