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
*/ | */ | ||||
public ClassFile getClassFile2() { return null; } | 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. | * Returns the uniform resource locator (URL) of the class file. | ||||
*/ | */ | ||||
void checkModify() throws RuntimeException { | void checkModify() throws RuntimeException { | ||||
if (isFrozen()) | 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. | // isModified() must return true after this method is invoked. | ||||
} | } |
import javassist.bytecode.*; | import javassist.bytecode.*; | ||||
import javassist.compiler.Javac; | import javassist.compiler.Javac; | ||||
import javassist.compiler.CompileError; | import javassist.compiler.CompileError; | ||||
import javassist.compiler.AccessorMaker; | |||||
import javassist.expr.ExprEditor; | import javassist.expr.ExprEditor; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.BufferedInputStream; | import java.io.BufferedInputStream; | ||||
private CtConstructor classInitializerCache; | private CtConstructor classInitializerCache; | ||||
private CtMethod methodsCache; | private CtMethod methodsCache; | ||||
private AccessorMaker accessors; | |||||
private FieldInitLink fieldInitializers; | private FieldInitLink fieldInitializers; | ||||
private Hashtable hiddenMethods; // must be synchronous | private Hashtable hiddenMethods; // must be synchronous | ||||
private int uniqueNumberSeed; | private int uniqueNumberSeed; | ||||
classPool = cp; | classPool = cp; | ||||
wasChanged = wasFrozen = false; | wasChanged = wasFrozen = false; | ||||
classfile = null; | classfile = null; | ||||
accessors = null; | |||||
fieldInitializers = null; | fieldInitializers = null; | ||||
hiddenMethods = null; | hiddenMethods = null; | ||||
uniqueNumberSeed = 0; | uniqueNumberSeed = 0; | ||||
methodsCache = null; | methodsCache = null; | ||||
} | } | ||||
public AccessorMaker getAccessorMaker() { | |||||
if (accessors == null) | |||||
accessors = new AccessorMaker(this); | |||||
return accessors; | |||||
} | |||||
public ClassFile getClassFile2() { | public ClassFile getClassFile2() { | ||||
if (classfile != null) | if (classfile != null) | ||||
return classfile; | return classfile; |
stackDepth = 0; | 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(). | /* used in add(). | ||||
*/ | */ | ||||
private Bytecode() { | private Bytecode() { |
* | * | ||||
* @param newCp the constant pool table used by the new copy. | * @param newCp the constant pool table used by the new copy. | ||||
* @param classnames pairs of replaced and substituted | * @param classnames pairs of replaced and substituted | ||||
* class names. | |||||
* class names. It can be <code>null</code>. | |||||
*/ | */ | ||||
public AttributeInfo copy(ConstPool newCp, Map classnames) { | public AttributeInfo copy(ConstPool newCp, Map classnames) { | ||||
return new ExceptionsAttribute(newCp, this, classnames); | return new ExceptionsAttribute(newCp, this, classnames); |
/* | |||||
* 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; | package javassist.compiler; | ||||
import javassist.CannotCompileException; | |||||
import javassist.NotFoundException; | |||||
public class CompileError extends Exception { | public class CompileError extends Exception { | ||||
private Lex lex; | private Lex lex; | ||||
private String reason; | private String reason; | ||||
lex = null; | lex = null; | ||||
} | } | ||||
public CompileError(CannotCompileException e) { | |||||
this(e.getReason()); | |||||
} | |||||
public CompileError(NotFoundException e) { | |||||
this("cannot find " + e.getMessage()); | |||||
} | |||||
public String getMessage() { | public String getMessage() { | ||||
return reason; | return reason; | ||||
} | } |
throw new CompileError(msg); | 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; | CtClass declClass = found.declaring; | ||||
MethodInfo minfo = found.info; | MethodInfo minfo = found.info; | ||||
String desc = minfo.getDescriptor(); | String desc = minfo.getDescriptor(); | ||||
throw new CompileError("no such a constructor"); | throw new CompileError("no such a constructor"); | ||||
} | } | ||||
else if ((acc & AccessFlag.PRIVATE) != 0) { | 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; | boolean popTarget = false; | ||||
bytecode.addInvokestatic(declClass, mname, desc); | bytecode.addInvokestatic(declClass, mname, desc); | ||||
} | } | ||||
else if (isSpecial) | |||||
else if (isSpecial) // if (isSpecial && notStatic(acc)) | |||||
bytecode.addInvokespecial(declClass, mname, desc); | bytecode.addInvokespecial(declClass, mname, desc); | ||||
else if (declClass.isInterface()) | else if (declClass.isInterface()) | ||||
bytecode.addInvokeinterface(declClass, mname, desc, count); | bytecode.addInvokeinterface(declClass, mname, desc, count); | ||||
setReturnType(desc, isStatic, popTarget); | 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 | else | ||||
return null; // cannot access this private method. | return null; // cannot access this private method. | ||||
} | } |