From 94ddad2e3fe57daa9978827d1f974996f6a12647 Mon Sep 17 00:00:00 2001 From: chiba Date: Tue, 4 May 2004 17:22:40 +0000 Subject: [PATCH] Now, the compiler accepts a method that calls a private method declared in an enclosing class. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@97 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/CtClass.java | 9 +- src/main/javassist/CtClassType.java | 11 ++ src/main/javassist/bytecode/Bytecode.java | 13 +++ .../bytecode/ExceptionsAttribute.java | 2 +- .../javassist/compiler/AccessorMaker.java | 101 ++++++++++++++++++ src/main/javassist/compiler/CompileError.java | 11 ++ .../javassist/compiler/MemberCodeGen.java | 61 ++++++++--- 7 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 src/main/javassist/compiler/AccessorMaker.java diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index f3e8963e..74f953be 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -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. } diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index e503abcb..de88c855 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -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; diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java index 0aa96649..ae6f1691 100644 --- a/src/main/javassist/bytecode/Bytecode.java +++ b/src/main/javassist/bytecode/Bytecode.java @@ -78,6 +78,19 @@ public class Bytecode implements Opcode { stackDepth = 0; } + /** + * Constructs a Bytecode object with an empty bytecode + * sequence. The initial values of max_stack and + * max_locals 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() { diff --git a/src/main/javassist/bytecode/ExceptionsAttribute.java b/src/main/javassist/bytecode/ExceptionsAttribute.java index 3f06376b..0a103452 100644 --- a/src/main/javassist/bytecode/ExceptionsAttribute.java +++ b/src/main/javassist/bytecode/ExceptionsAttribute.java @@ -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 null. */ public AttributeInfo copy(ConstPool newCp, Map classnames) { return new ExceptionsAttribute(newCp, this, classnames); diff --git a/src/main/javassist/compiler/AccessorMaker.java b/src/main/javassist/compiler/AccessorMaker.java new file mode 100644 index 00000000..f353a408 --- /dev/null +++ b/src/main/javassist/compiler/AccessorMaker.java @@ -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 clazz. + * If the private method is static, + * accDesc must be equal to desc. + * + * @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; + } +} diff --git a/src/main/javassist/compiler/CompileError.java b/src/main/javassist/compiler/CompileError.java index 01c51a43..5c877da8 100644 --- a/src/main/javassist/compiler/CompileError.java +++ b/src/main/javassist/compiler/CompileError.java @@ -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; } diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index e8c5ba3b..549626db 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -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. } -- 2.39.5