/* * 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 com.fr.third.javassist; import com.fr.third.javassist.bytecode.SyntheticAttribute; import com.fr.third.javassist.compiler.JvstCodeGen; import java.util.Hashtable; import com.fr.third.javassist.CtMethod.ConstParameter; class CtNewWrappedMethod { private static final String addedWrappedMethod = "_added_m$"; public static com.fr.third.javassist.CtMethod wrapped(com.fr.third.javassist.CtClass returnType, String mname, com.fr.third.javassist.CtClass[] parameterTypes, com.fr.third.javassist.CtClass[] exceptionTypes, com.fr.third.javassist.CtMethod body, ConstParameter constParam, com.fr.third.javassist.CtClass declaring) throws com.fr.third.javassist.CannotCompileException { com.fr.third.javassist.CtMethod mt = new com.fr.third.javassist.CtMethod(returnType, mname, parameterTypes, declaring); mt.setModifiers(body.getModifiers()); try { mt.setExceptionTypes(exceptionTypes); } catch (NotFoundException e) { throw new com.fr.third.javassist.CannotCompileException(e); } com.fr.third.javassist.bytecode.Bytecode code = makeBody(declaring, declaring.getClassFile2(), body, parameterTypes, returnType, constParam); com.fr.third.javassist.bytecode.MethodInfo minfo = mt.getMethodInfo2(); minfo.setCodeAttribute(code.toCodeAttribute()); // a stack map has been already created. return mt; } static com.fr.third.javassist.bytecode.Bytecode makeBody(com.fr.third.javassist.CtClass clazz, com.fr.third.javassist.bytecode.ClassFile classfile, com.fr.third.javassist.CtMethod wrappedBody, com.fr.third.javassist.CtClass[] parameters, com.fr.third.javassist.CtClass returnType, ConstParameter cparam) throws com.fr.third.javassist.CannotCompileException { boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers()); com.fr.third.javassist.bytecode.Bytecode code = new com.fr.third.javassist.bytecode.Bytecode(classfile.getConstPool(), 0, 0); int stacksize = makeBody0(clazz, classfile, wrappedBody, isStatic, parameters, returnType, cparam, code); code.setMaxStack(stacksize); code.setMaxLocals(isStatic, parameters, 0); return code; } /* The generated method body does not need a stack map table * because it does not contain a branch instruction. */ protected static int makeBody0(com.fr.third.javassist.CtClass clazz, com.fr.third.javassist.bytecode.ClassFile classfile, com.fr.third.javassist.CtMethod wrappedBody, boolean isStatic, com.fr.third.javassist.CtClass[] parameters, com.fr.third.javassist.CtClass returnType, ConstParameter cparam, com.fr.third.javassist.bytecode.Bytecode code) throws com.fr.third.javassist.CannotCompileException { if (!(clazz instanceof com.fr.third.javassist.CtClassType)) throw new com.fr.third.javassist.CannotCompileException("bad declaring class" + clazz.getName()); if (!isStatic) code.addAload(0); int stacksize = compileParameterList(code, parameters, (isStatic ? 0 : 1)); int stacksize2; String desc; if (cparam == null) { stacksize2 = 0; desc = ConstParameter.defaultDescriptor(); } else { stacksize2 = cparam.compile(code); desc = cparam.descriptor(); } checkSignature(wrappedBody, desc); String bodyname; try { bodyname = addBodyMethod((com.fr.third.javassist.CtClassType)clazz, classfile, wrappedBody); /* if an exception is thrown below, the method added above * should be removed. (future work :<) */ } catch (com.fr.third.javassist.bytecode.BadBytecode e) { throw new com.fr.third.javassist.CannotCompileException(e); } if (isStatic) code.addInvokestatic(com.fr.third.javassist.bytecode.Bytecode.THIS, bodyname, desc); else code.addInvokespecial(com.fr.third.javassist.bytecode.Bytecode.THIS, bodyname, desc); compileReturn(code, returnType); // consumes 2 stack entries if (stacksize < stacksize2 + 2) stacksize = stacksize2 + 2; return stacksize; } private static void checkSignature(com.fr.third.javassist.CtMethod wrappedBody, String descriptor) throws com.fr.third.javassist.CannotCompileException { if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor())) throw new com.fr.third.javassist.CannotCompileException( "wrapped method with a bad signature: " + wrappedBody.getDeclaringClass().getName() + '.' + wrappedBody.getName()); } private static String addBodyMethod(com.fr.third.javassist.CtClassType clazz, com.fr.third.javassist.bytecode.ClassFile classfile, com.fr.third.javassist.CtMethod src) throws com.fr.third.javassist.bytecode.BadBytecode, CannotCompileException { Hashtable bodies = clazz.getHiddenMethods(); String bodyname = (String)bodies.get(src); if (bodyname == null) { do { bodyname = addedWrappedMethod + clazz.getUniqueNumber(); } while (classfile.getMethod(bodyname) != null); com.fr.third.javassist.ClassMap map = new ClassMap(); map.put(src.getDeclaringClass().getName(), clazz.getName()); com.fr.third.javassist.bytecode.MethodInfo body = new com.fr.third.javassist.bytecode.MethodInfo(classfile.getConstPool(), bodyname, src.getMethodInfo2(), map); int acc = body.getAccessFlags(); body.setAccessFlags(com.fr.third.javassist.bytecode.AccessFlag.setPrivate(acc)); body.addAttribute(new SyntheticAttribute(classfile.getConstPool())); // a stack map is copied. rebuilding it is not needed. classfile.addMethod(body); bodies.put(src, bodyname); CtMember.Cache cache = clazz.hasMemberCache(); if (cache != null) cache.addMethod(new CtMethod(body, clazz)); } return bodyname; } /* compileParameterList() returns the stack size used * by the produced code. * * @param regno the index of the local variable in which * the first argument is received. * (0: static method, 1: regular method.) */ static int compileParameterList(com.fr.third.javassist.bytecode.Bytecode code, com.fr.third.javassist.CtClass[] params, int regno) { return JvstCodeGen.compileParameterList(code, params, regno); } /* * The produced codes cosume 1 or 2 stack entries. */ private static void compileReturn(com.fr.third.javassist.bytecode.Bytecode code, com.fr.third.javassist.CtClass type) { if (type.isPrimitive()) { CtPrimitiveType pt = (CtPrimitiveType)type; if (pt != CtClass.voidType) { String wrapper = pt.getWrapperName(); code.addCheckcast(wrapper); code.addInvokevirtual(wrapper, pt.getGetMethodName(), pt.getGetMethodDescriptor()); } code.addOpcode(pt.getReturnOp()); } else { code.addCheckcast(type); code.addOpcode(com.fr.third.javassist.bytecode.Bytecode.ARETURN); } } }