/*
* 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 io.github.proxyhotswap.javassist;
import io.github.proxyhotswap.javassist.CtMethod.ConstParameter;
import io.github.proxyhotswap.javassist.bytecode.*;
import io.github.proxyhotswap.javassist.compiler.JvstCodeGen;
import java.util.Hashtable;
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
{
Hashtable bodies = clazz.getHiddenMethods();
String bodyname = (String)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);
}
}
}