Думаю, я пытаюсь сделать что-то относительно простое. Возьмем, к примеру, следующий байт-код Java для метода doSomething(int):
public java.lang.String doSomething(int i);
0 iload_1 [i]
1 invokestatic MyHelper.doSomething(int) : Java.lang.String
4 areturn
Этот байт-код в значительной степени перенаправляет вызов только статическому помощнику.
Что я хочу сделать сейчас, так это заменить invokestatic на invokedynamic с помощью Javassist. Я знаю, как это сделать с помощью ASM, поэтому просто предположу, что хочу знать, как это работает, из чистого любопытства. Вот несколько вопросов, которые у меня есть:
1) Верно ли следующее: я не могу использовать методы javassist CtMethod.instrument() или CtMethod.insertAt(), потому что эти методы ожидают строку, содержащую допустимое выражение Java, и я не могу написать invokedynamic в синтаксисе Java?
2) Параметры для invokestatic обрабатываются точно так же, как параметры для invokevirtual или invokestatic, верно? Я имею в виду, что вы помещаете параметры в стек перед вызовом dynamic точно так же, как если бы вы делали это для вызова виртуала?
3) Есть ли пример кода (или вы могли бы придумать), который использует Javassist для создания байт-кода invokedynamic?
Это то, что я знаю до сих пор: вы можете создать объект Bytecode, который имеет метод addInvokedynamic(). Но это предполагает индекс BootstrapMethod в BootstrapMethodsAttribute. BootstrapMethod, в свою очередь, ожидает индекс информации об дескрипторе метода в пуле констант, который хочет ссылку на метод и так далее. Таким образом, вы должны сами управлять всеми постоянными записями пула. Это нормально, но я обеспокоен тем, что я не понимаю это правильно и добавлю странные проблемы позже. Есть ли более простой способ сделать это (вспомогательный метод или около того)? Код, который у меня есть, примерно выглядит примерно так (на самом деле я не «переписываю» приведенный выше invokestatic, но:
void transform(CtMethod ctmethod, String originalCallDescriptor) {
MethodInfo mInfo = ctmethod.getMethodInfo();
ConstPool pool = ctmethod.getDeclaringClass().getClassFile().getConstPool();
/* add info about the static bootstrap method to the constant pool*/
int mRefIdx = /*somehow create a method reference entry*/
int mHandleIdx = constPool.addMethodHandleInfo(ConstPool.REF_invokeStatic, mRefIdx);
/* create bootstrap methods attribute; there can only be one per class file! */
BootstrapMethodsAttribute.BootstrapMethod[] bms = new BootstrapMethodsAttribute.BootstrapMethod[] {
new BootstrapMethodsAttribute.BootstrapMethod(mHandleIdx, new int[] {})
};
BootstrapMethodsAttribute bmsAttribute = new BootstrapMethodsAttribute(constPool, bms);
mInfo.addAttribute(bmsAttribute);
//... and then later, finally
Bytecode bc = new Bytecode(constPool);
... push parameters ...
bc.addInvokedynamic(0 /*index in bms array*/, mInfo.getName(), originalCallDescriptor);
//replace the original method body with the one containing invokedynamic
mInfo.removeCodeAttribute();
mInfo.setCodeAttribute(bc.toCodeAttribute());
}
Большое спасибо за вашу помощь и время!