/*
 * 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.HashMap;
import java.util.Map;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
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.ExceptionsAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.SyntheticAttribute;

AccessorMaker maintains accessors to private members of an enclosing class. It is necessary for compiling a method in an inner class.
/** * 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 Map<String,Object> accessors; static final String lastParamType = "javassist.runtime.Inner"; public AccessorMaker(CtClass c) { clazz = c; uniqueNumber = 1; accessors = new HashMap<String,Object>(); } public String getConstructor(CtClass c, String desc, MethodInfo orig) throws CompileError { String key = "<init>:" + desc; String consDesc = (String)accessors.get(key); if (consDesc != null) return consDesc; // already exists. consDesc = Descriptor.appendParameter(lastParamType, desc); ClassFile cf = clazz.getClassFile(); // turn on the modified flag. try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); MethodInfo minfo = new MethodInfo(cp, MethodInfo.nameInit, consDesc); minfo.setAccessFlags(0); minfo.addAttribute(new SyntheticAttribute(cp)); ExceptionsAttribute ea = orig.getExceptionsAttribute(); if (ea != null) minfo.addAttribute(ea.copy(cp, null)); CtClass[] params = Descriptor.getParameterTypes(desc, pool); Bytecode code = new Bytecode(cp); code.addAload(0); int regno = 1; for (int i = 0; i < params.length; ++i) regno += code.addLoad(regno, params[i]); code.setMaxLocals(regno + 1); // the last parameter is added. code.addInvokespecial(clazz, MethodInfo.nameInit, desc); code.addReturn(null); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } accessors.put(key, consDesc); return consDesc; }
Returns the name of the method for accessing a private method.
Params:
  • name – the name of the private method.
  • desc – the descriptor of the private method.
  • accDesc – the descriptor of the accessor method. The first parameter type is clazz. If the private method is static, accDesc must be identical to desc.
  • orig – the method info of the private method.
Returns:
/** * 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 identical 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. accName = findAccessorName(cf); 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; }
Returns the method_info representing the added getter.
/** * Returns the method_info representing the added getter. */
public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) throws CompileError { String fieldName = finfo.getName(); String key = fieldName + ":getter"; Object res = accessors.get(key); if (res != null) return (MethodInfo)res; // already exists. ClassFile cf = clazz.getClassFile(); // turn on the modified flag. String accName = findAccessorName(cf); try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); String fieldType = finfo.getDescriptor(); String accDesc; if (is_static) accDesc = "()" + fieldType; else accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; MethodInfo minfo = new MethodInfo(cp, accName, accDesc); minfo.setAccessFlags(AccessFlag.STATIC); minfo.addAttribute(new SyntheticAttribute(cp)); Bytecode code = new Bytecode(cp); if (is_static) { code.addGetstatic(Bytecode.THIS, fieldName, fieldType); } else { code.addAload(0); code.addGetfield(Bytecode.THIS, fieldName, fieldType); code.setMaxLocals(1); } code.addReturn(Descriptor.toCtClass(fieldType, pool)); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); accessors.put(key, minfo); return minfo; } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } }
Returns the method_info representing the added setter.
/** * Returns the method_info representing the added setter. */
public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) throws CompileError { String fieldName = finfo.getName(); String key = fieldName + ":setter"; Object res = accessors.get(key); if (res != null) return (MethodInfo)res; // already exists. ClassFile cf = clazz.getClassFile(); // turn on the modified flag. String accName = findAccessorName(cf); try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); String fieldType = finfo.getDescriptor(); String accDesc; if (is_static) accDesc = "(" + fieldType + ")V"; else accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; MethodInfo minfo = new MethodInfo(cp, accName, accDesc); minfo.setAccessFlags(AccessFlag.STATIC); minfo.addAttribute(new SyntheticAttribute(cp)); Bytecode code = new Bytecode(cp); int reg; if (is_static) { reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); code.addPutstatic(Bytecode.THIS, fieldName, fieldType); } else { code.addAload(0); reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) + 1; code.addPutfield(Bytecode.THIS, fieldName, fieldType); } code.addReturn(null); code.setMaxLocals(reg); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); accessors.put(key, minfo); return minfo; } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } } private String findAccessorName(ClassFile cf) { String accName; do { accName = "access$" + uniqueNumber++; } while (cf.getMethod(accName) != null); return accName; } }