Skip to content

Commit

Permalink
Merge pull request #480 from likey3/master
Browse files Browse the repository at this point in the history
support bootstrap method coping when using code coping
  • Loading branch information
chibash authored May 3, 2024
2 parents 7302b8b + 9adc1a3 commit 204d678
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 38 deletions.
103 changes: 81 additions & 22 deletions src/main/javassist/bytecode/BootstrapMethodsAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

public class BootstrapMethodsAttribute extends AttributeInfo {
Expand Down Expand Up @@ -35,6 +36,26 @@ public BootstrapMethod(int method, int[] args) {
* <code>bootstrap_arguments</code>.
*/
public int[] arguments;

/**
* Makes a copy. Class names are replaced according to the
* * given <code>Map</code> object.
*
* @param srcCp the constant pool table from the source
* @param destCp the constant pool table used bt new copy
* @param classnames pairs of replaced and substituted class names.
*
* @return new BootstrapMethod
*/
protected BootstrapMethod copy(ConstPool srcCp, ConstPool destCp, Map<String,String> classnames) {
int newMethodRef = srcCp.copy(methodRef, destCp, classnames);
int[] newArguments = new int[arguments.length];

for (int i = 0; i < arguments.length; i++)
newArguments[i] = srcCp.copy(arguments[i], destCp, classnames);

return new BootstrapMethod(newMethodRef, newArguments);
}
}

BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in)
Expand All @@ -51,25 +72,8 @@ public BootstrapMethod(int method, int[] args) {
*/
public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) {
super(cp, tag);
int size = 2;
for (int i = 0; i < methods.length; i++)
size += 4 + methods[i].arguments.length * 2;

byte[] data = new byte[size];
ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods
int pos = 2;
for (int i = 0; i < methods.length; i++) {
ByteArray.write16bit(methods[i].methodRef, data, pos);
ByteArray.write16bit(methods[i].arguments.length, data, pos + 2);
int[] args = methods[i].arguments;
pos += 4;
for (int k = 0; k < args.length; k++) {
ByteArray.write16bit(args[k], data, pos);
pos += 2;
}
}

set(data);
set(convertMethodsToBytes(methods));
}

/**
Expand Down Expand Up @@ -113,12 +117,67 @@ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) {
BootstrapMethod[] methods = getMethods();
ConstPool thisCp = getConstPool();
for (int i = 0; i < methods.length; i++) {
BootstrapMethod m = methods[i];
m.methodRef = thisCp.copy(m.methodRef, newCp, classnames);
for (int k = 0; k < m.arguments.length; k++)
m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames);
methods[i] = methods[i].copy(thisCp, newCp, classnames);
}

return new BootstrapMethodsAttribute(newCp, methods);
}

/**
* add bootstrap method from given <code>ConstPool</code> and <code>BootstrapMethod</code>,
* and add it to the specified index. Class names are replaced according to the
* given <code>Map</code> object.
*
* <p>
* if the index less than 0 or large than the origin method length, then throw <code>RuntimeException</code>;<br>
* if the index large or equals to 0 and less or equals to the origin method length,
* then replace the origin method with the new <code>BootstrapMethod srcBm</code> ;<br>
* if the index equals to the origin method length, then append the new <code>BootstrapMethod srcBm</code> at
* the origin methods tail.
* </p>
*
* @param srcCp the constant pool table of source.
* @param srcBm the bootstrap method of source
* @param index the new method index on bootstrap methods
* @param classnames pairs of replaced and substituted
* class names.
*/
public void addMethod(ConstPool srcCp, BootstrapMethod srcBm, int index, Map<String,String> classnames) {
BootstrapMethod[] methods = getMethods();

if (index < 0 || index > methods.length) {
throw new RuntimeException("index out of range");
}

if (index == methods.length) {
BootstrapMethod[] newBmArray = new BootstrapMethod[methods.length + 1];
System.arraycopy(methods, 0, newBmArray, 0, methods.length);
methods = newBmArray;
}

methods[index] = srcBm.copy(srcCp, getConstPool(), classnames);
set(convertMethodsToBytes(methods));
}

private static byte[] convertMethodsToBytes(BootstrapMethod[] methods) {
int size = 2;
for (int i = 0; i < methods.length; i++)
size += 4 + methods[i].arguments.length * 2;

byte[] data = new byte[size];
ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods
int pos = 2;
for (int i = 0; i < methods.length; i++) {
ByteArray.write16bit(methods[i].methodRef, data, pos);
ByteArray.write16bit(methods[i].arguments.length, data, pos + 2);
int[] args = methods[i].arguments;
pos += 4;
for (int k = 0; k < args.length; k++) {
ByteArray.write16bit(args[k], data, pos);
pos += 2;
}
}

return data;
}
}
149 changes: 141 additions & 8 deletions src/main/javassist/bytecode/CodeAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package javassist.bytecode;

import javassist.*;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
Expand Down Expand Up @@ -76,8 +78,7 @@ public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code,
* class names.
*/
private CodeAttribute(ConstPool cp, CodeAttribute src, Map<String,String> classnames)
throws BadBytecode
{
throws BadBytecode, NotFoundException, CannotCompileException {
super(cp, tag);

maxStack = src.getMaxStack();
Expand Down Expand Up @@ -139,6 +140,10 @@ public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames)
}
catch (BadBytecode e) {
throw new RuntimeCopyException("bad bytecode. fatal?");
} catch (NotFoundException e) {
throw new RuntimeException(e);
} catch (CannotCompileException e) {
throw new RuntimeException(e);
}
}

Expand Down Expand Up @@ -324,7 +329,7 @@ public AttributeInfo getAttribute(String name) {
*
* @param smt the stack map table added to this code attribute.
* If it is null, a new stack map is not added.
* Only the old stack map is removed.
* Only the old stack map is removed.
*/
public void setAttribute(StackMapTable smt) {
AttributeInfo.remove(attributes, StackMapTable.tag);
Expand Down Expand Up @@ -352,21 +357,20 @@ public void setAttribute(StackMap sm) {
*/
private byte[] copyCode(ConstPool destCp, Map<String,String> classnames,
ExceptionTable etable, CodeAttribute destCa)
throws BadBytecode
{
throws BadBytecode, NotFoundException, CannotCompileException {
int len = getCodeLength();
byte[] newCode = new byte[len];
destCa.info = newCode;

LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(),
newCode, destCp, classnames);
return LdcEntry.doit(newCode, ldc, etable, destCa);
}

private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
ConstPool srcCp, byte[] newcode,
ConstPool destCp, Map<String,String> classnameMap)
throws BadBytecode
{
ConstPool destCp, Map<String,String> classnameMap)
throws BadBytecode, NotFoundException, CannotCompileException {
int i2, index;
LdcEntry ldcEntry = null;

Expand Down Expand Up @@ -415,6 +419,7 @@ private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
case INVOKEDYNAMIC :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
copyBootstrapMethod(srcCp, destCp, i + 1, code, newcode, classnameMap);
newcode[i + 3] = 0;
newcode[i + 4] = 0;
break;
Expand All @@ -434,6 +439,134 @@ private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
return ldcEntry;
}

/**
* Copy the Bootstrap method of the specified index referenced in the source <code>InvokeDynamic</code> directive
* to the specified index in the destination Boostrap Attribute.<br>
* if the Bootstrap Attribute does not exist in the destination class, create a new Bootstrap Attribute; <br>
* if the destination Bootstrap Method already exists at the specified index method,
* the method at that position will be overwritten, otherwise it will be added
* at the end of the destination Bootstrap method.
*
* @param srcCp the constant pool table of source
* @param destCp the constant pool table of destination
* @param codeIndex the index of the invoke dynamic first parameter in code array
* @param srcCode the code array of source
* @param newCode the code array of destination
* @param classnameMap pairs of replaced and substituted class names.
*
* @throws NotFoundException this exception thrown when the class
* cannot be found in the default <code>ClassPool</code>
* @throws CannotCompileException this exception thrown from the method
* {@link #copyInvokeStaticMethod(CtClass, ConstPool,
* BootstrapMethodsAttribute.BootstrapMethod, CtClass, Map)}
*/
private static void copyBootstrapMethod(ConstPool srcCp, ConstPool destCp, int codeIndex, byte[] srcCode,
byte[] newCode, Map<String,String> classnameMap)
throws NotFoundException, CannotCompileException {
ClassPool classPool = ClassPool.getDefault();
CtClass srcCc = classPool.get(srcCp.getClassName());
CtClass destCc = classPool.get(destCp.getClassName());
ClassFile srcCf = srcCc.getClassFile();
ClassFile destCf = destCc.getClassFile();
BootstrapMethodsAttribute srcBma = (BootstrapMethodsAttribute)
srcCf.getAttribute(BootstrapMethodsAttribute.tag);

// if source class does not have bootstrap attribute then stop copy
if (srcBma == null) {
return;
}

BootstrapMethodsAttribute destBma = (BootstrapMethodsAttribute)
destCf.getAttribute(BootstrapMethodsAttribute.tag);

int srcCpIndex = ((srcCode[codeIndex] & 0xff) << 8) | (srcCode[codeIndex + 1] & 0xff);
int destCpIndex = ((newCode[codeIndex] & 0xff) << 8) | (newCode[codeIndex + 1] & 0xff);
int srcBmIndex = srcCp.getInvokeDynamicBootstrap(srcCpIndex);
int destBmIndex = destCp.getInvokeDynamicBootstrap(destCpIndex);

// if source class does not have bootstrap attribute, then create bootstrap attribute
if (destBma == null) {
destBma = new BootstrapMethodsAttribute(destCp,
new BootstrapMethodsAttribute.BootstrapMethod[0]);
destCf.addAttribute(destBma);
}

BootstrapMethodsAttribute.BootstrapMethod srcBm = srcBma.getMethods()[srcBmIndex];
destBma.addMethod(srcCp, srcBm, destBmIndex, classnameMap);

copyInvokeStaticMethod(srcCc, srcCp, srcBm, destCc, classnameMap);
}

/**
* Copy the static methods referenced by the bootstrap method in this class (such as some lambda methods).<br>
* If the source method exists in the destination class, it will be ignored.
*
* @param srcCc source class
* @param srcCp constant pool table of source class
* @param srcBm source method to be copied
* @param destCc destination class
* @param classnameMap irs of replaced and substituted class names.
*
* @throws CannotCompileException thrown by {@link CtNewMethod#copy(CtMethod, CtClass, ClassMap)}
* or{@link CtClass#addMethod(CtMethod)}
*/
private static void copyInvokeStaticMethod(CtClass srcCc, ConstPool srcCp,
BootstrapMethodsAttribute.BootstrapMethod srcBm, CtClass destCc,
Map<String, String> classnameMap) throws CannotCompileException {
for (int argument : srcBm.arguments) {
ConstInfo constInfo = srcCp.getItem(argument);

if (!(constInfo instanceof MethodHandleInfo)) continue;

MethodHandleInfo methodHandleInfo = (MethodHandleInfo) constInfo;
if (ConstPool.REF_invokeStatic != methodHandleInfo.refKind) continue;

String methodRefClassName = srcCp.getMethodrefClassName(methodHandleInfo.refIndex);
if (methodRefClassName == null || !methodRefClassName.equals(srcCc.getName())) continue;

String staticMethodName = srcCp.getMethodrefName(methodHandleInfo.refIndex);
String staticMethodSignature = srcCp.getMethodrefType(methodHandleInfo.refIndex);
CtMethod srcMethod = getStaticCtMethod(srcCc, staticMethodName, staticMethodSignature);

if (!checkStaticMethodExisted(destCc, staticMethodName, staticMethodSignature)) {
ClassMap classMap = new ClassMap();
classMap.putAll(classnameMap);

CtMethod ctMethod = CtNewMethod.copy(srcMethod, destCc, classMap);
destCc.addMethod(ctMethod);
}
}
}

private static CtMethod getStaticCtMethod(CtClass ctClass, String staticMethodName, String staticMethodSignature) {
CtMethod srcMethod = null;
for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) {
if (Modifier.isStatic(declaredMethod.getModifiers())
&& declaredMethod.getName().equals(staticMethodName)
&& declaredMethod.getSignature().equals(staticMethodSignature)) {
srcMethod = declaredMethod;
break;
}
}

if (srcMethod == null) {
throw new RuntimeException("Can not found static method:" + staticMethodName);
}
return srcMethod;
}

private static boolean checkStaticMethodExisted(CtClass ctClass, String staticMethodName, String staticMethodSignature) {
for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) {
if (Modifier.isStatic(declaredMethod.getModifiers())
&& declaredMethod.getName().equals(staticMethodName)
&& declaredMethod.getSignature().equals(staticMethodSignature)) {
return true;
}
}

return false;
}

private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
byte[] newcode, ConstPool destCp,
Map<String,String> classnameMap) {
Expand Down
Loading

0 comments on commit 204d678

Please sign in to comment.