/* * 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; import java.util.Map; import javassist.CtMethod.ConstParameter; import javassist.bytecode.AccessFlag; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; import javassist.bytecode.MethodInfo; import javassist.bytecode.SyntheticAttribute; import javassist.compiler.JvstCodeGen; class CtNewWrappedMethod { private static final String addedWrappedMethod = "_added_m$"; public static CtMethod wrapped(CtClass returnType, String mname, CtClass[] parameterTypes, CtClass[] exceptionTypes, CtMethod body, ConstParameter constParam, CtClass declaring) throws CannotCompileException { CtMethod mt = new CtMethod(returnType, mname, parameterTypes, declaring); mt.setModifiers(body.getModifiers()); try { mt.setExceptionTypes(exceptionTypes); } catch (NotFoundException e) { throw new CannotCompileException(e); } Bytecode code = makeBody(declaring, declaring.getClassFile2(), body, parameterTypes, returnType, constParam); MethodInfo minfo = mt.getMethodInfo2(); minfo.setCodeAttribute(code.toCodeAttribute()); // a stack map has been already created. return mt; } static Bytecode makeBody(CtClass clazz, ClassFile classfile, CtMethod wrappedBody, CtClass[] parameters, CtClass returnType, ConstParameter cparam) throws CannotCompileException { boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers()); Bytecode code = new 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(CtClass clazz, ClassFile classfile, CtMethod wrappedBody, boolean isStatic, CtClass[] parameters, CtClass returnType, ConstParameter cparam, Bytecode code) throws CannotCompileException { if (!(clazz instanceof CtClassType)) throw new 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((CtClassType)clazz, classfile, wrappedBody); /* if an exception is thrown below, the method added above * should be removed. (future work :<) */ } catch (BadBytecode e) { throw new CannotCompileException(e); } if (isStatic) code.addInvokestatic(Bytecode.THIS, bodyname, desc); else code.addInvokespecial(Bytecode.THIS, bodyname, desc); compileReturn(code, returnType); // consumes 2 stack entries if (stacksize < stacksize2 + 2) stacksize = stacksize2 + 2; return stacksize; } private static void checkSignature(CtMethod wrappedBody, String descriptor) throws CannotCompileException { if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor())) throw new CannotCompileException( "wrapped method with a bad signature: " + wrappedBody.getDeclaringClass().getName() + '.' + wrappedBody.getName()); } private static String addBodyMethod(CtClassType clazz, ClassFile classfile, CtMethod src) throws BadBytecode, CannotCompileException { Map bodies = clazz.getHiddenMethods(); String bodyname = bodies.get(src); if (bodyname == null) { do { bodyname = addedWrappedMethod + clazz.getUniqueNumber(); } while (classfile.getMethod(bodyname) != null); ClassMap map = new ClassMap(); map.put(src.getDeclaringClass().getName(), clazz.getName()); MethodInfo body = new MethodInfo(classfile.getConstPool(), bodyname, src.getMethodInfo2(), map); int acc = body.getAccessFlags(); body.setAccessFlags(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(Bytecode code, CtClass[] params, int regno) { return JvstCodeGen.compileParameterList(code, params, regno); } /* * The produced codes cosume 1 or 2 stack entries. */ private static void compileReturn(Bytecode code, 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(Bytecode.ARETURN); } } }