declared in an enclosing class. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@97 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -203,6 +203,13 @@ public abstract class CtClass { | |||
*/ | |||
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. | |||
*/ | |||
@@ -227,7 +234,7 @@ public abstract class CtClass { | |||
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. | |||
} |
@@ -18,6 +18,7 @@ package javassist; | |||
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; | |||
@@ -44,6 +45,8 @@ class CtClassType extends CtClass { | |||
private CtConstructor classInitializerCache; | |||
private CtMethod methodsCache; | |||
private AccessorMaker accessors; | |||
private FieldInitLink fieldInitializers; | |||
private Hashtable hiddenMethods; // must be synchronous | |||
private int uniqueNumberSeed; | |||
@@ -53,6 +56,7 @@ class CtClassType extends CtClass { | |||
classPool = cp; | |||
wasChanged = wasFrozen = false; | |||
classfile = null; | |||
accessors = null; | |||
fieldInitializers = null; | |||
hiddenMethods = null; | |||
uniqueNumberSeed = 0; | |||
@@ -104,6 +108,13 @@ class CtClassType extends CtClass { | |||
methodsCache = null; | |||
} | |||
public AccessorMaker getAccessorMaker() { | |||
if (accessors == null) | |||
accessors = new AccessorMaker(this); | |||
return accessors; | |||
} | |||
public ClassFile getClassFile2() { | |||
if (classfile != null) | |||
return classfile; |
@@ -78,6 +78,19 @@ public class Bytecode implements Opcode { | |||
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() { |
@@ -64,7 +64,7 @@ public class ExceptionsAttribute extends AttributeInfo { | |||
* | |||
* @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); |
@@ -0,0 +1,101 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -15,6 +15,9 @@ | |||
package javassist.compiler; | |||
import javassist.CannotCompileException; | |||
import javassist.NotFoundException; | |||
public class CompileError extends Exception { | |||
private Lex lex; | |||
private String reason; | |||
@@ -29,6 +32,14 @@ public class CompileError extends Exception { | |||
lex = null; | |||
} | |||
public CompileError(CannotCompileException e) { | |||
this(e.getReason()); | |||
} | |||
public CompileError(NotFoundException e) { | |||
this("cannot find " + e.getMessage()); | |||
} | |||
public String getMessage() { | |||
return reason; | |||
} |
@@ -351,6 +351,16 @@ public class MemberCodeGen extends CodeGen { | |||
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(); | |||
@@ -362,11 +372,24 @@ public class MemberCodeGen extends CodeGen { | |||
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; | |||
@@ -386,7 +409,7 @@ public class MemberCodeGen extends CodeGen { | |||
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); | |||
@@ -399,12 +422,26 @@ public class MemberCodeGen extends CodeGen { | |||
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. | |||
} |