인라인 메서드 본문에서 invokevirtual에 대한 예상치 못한 지침 및 매개 변수
http://asm.ow2.org/current/asm-transformations.pdf 의 "3.2.6 Inline Method"에있는 샘플 코드를 따라 MethodNode를 호출 사이트에 인라인했습니다.
내 문제는 인라인 후 생성 된 바이트 코드에 예기치 않은 지침이 표시된다는 것입니다 (이 바이트 코드는 내 코드와 일치하지 않습니다).이 문제 ifeq
는 인라인 메서드 본문 뒤에 있고 스택의 변수가 xLoad에 의해로드되는 경우에만 존재합니다 .
여전히 문제의 근본 원인을 찾지 못했습니다. 이제 최소한의 코드로 재현하기 위해 불필요한 코드를 모두 제거하기 시작했습니다. 좋은 제안이있는 사람은 누구나 환영합니다.
내 기존 창립 중 하나는 다음과 같습니다. 문제는 Frame과 관련이 없습니다. 왜냐하면 Configuration for ClassRewiter가 COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS
있고 Configuration for ClassReader 일 때 문제가 여전히 존재 하기 때문입니다.ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES
문제를 단순화하기 위해 수신자의 본문은 다음과 같습니다.
public invokeExact(Ljava/lang/String;)Z
ICONST_0
IRETURN
그리고 발신자는 :
public String invokeExact(String a, String b){
boolean flag = _guard.invokeExact(a);
if(flag)
{
return a;
}
return b;
}
. MethodWriter에서 호출자의 해당 바이트 코드 조작 추적은 다음과 같습니다.
public java.lang.String invokeExact(java.lang.String, java.lang.String)
....
4: aload_1
5: astore_3
6: astore 4
8: iconst_0
visitJumpInsn goto L1029004533
//visitmax() empty implementation.
//visitEnd() Empty implementation.
visitlabel L1029004533 // This label is newly created once inlining starts, but is visited until the end of inlining as the target of all xReturn instructions in the Callee's method body.
visitVarInsn istore 5
visitVarInsn iload 5
visitJumpInsn ifeq L980604133
visitVarInsn aload 1
visitInsn areturn
visitLabel L980604133
visitVarInsn aload 2
visitInsn areturn
마지막으로 생성 된 클래스 파일은 다음과 같습니다.
public java.lang.String invokeExact(java.lang.String, java.lang.String);
stack=2, locals=6, args_size=3
0: aload_0
1: getfield #17 // Field _guard:Ltest/code/jit/asm/simple/MHGuard;
4: aload_1
5: astore_3
6: astore 4
8: iconst_0
**9: goto 9
12: fconst_0
13: iconst_2**
14: iload 5
16: ifeq 21
19: aload_1
20: areturn
21: aload_2
22: areturn
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 12
locals = [ class test/code/jit/asm/simple/GWTSample, class java/lang/String, class java/lang/String, class java/lang/String, class test/code/jit/asm/simple/MHGuard ]
stack = [ int ]
frame_type = 252 /* append */
offset_delta = 8
locals = [ int ]
# 9, # 12 및 # 13이 잘못되었습니다.
내 코드의 일부는 다음과 같습니다 (주말에도 계속해서 코드를 간단하게 만들 것입니다).
public class MethodCallInliner extends LocalVariablesSorter {
protected MethodContext _context;
private IPlugin _plugin;
public MethodCallInliner(int access, String desc, MethodContext context){
// context.getRawMV() return a Class MethodWriter.
super(Opcodes.ASM5, access, desc, context.getRawMV());
_context = context;
//_fieldVisitor = new FieldManipulationVisitor(mv, context);
_plugin = NameMappingService.get().getPlugin();
//removed some unncessary codes..
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if(opcode != Opcodes.INVOKEVIRTUAL){
mv.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
MethodNode mn = _plugin.map(owner, name, desc, _context, this);
if(mn == null){
mv.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
//ASMUtil.debug(mn); //to double confirm the mn content is correct.
performInline(ASMUtil.isStaticMethod(mn)?Opcodes.INVOKESTATIC:Opcodes.INVOKEVIRTUAL, owner, desc, mn);
_plugin.postProcess(mn, this, _context);
}
protected void performInline(int opcode, String owner, String desc, MethodNode mn){
Remapper remapper = Mapper.getMapper(_context, _context.getReceiverFieldName());
mn.instructions.resetLabels();
Label end = new Label();
System.out.println("++"+end.toString());
_context.beginInline();
mn.accept(new InliningAdapter(this,
opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc,
remapper, end, _context));
_context.endInline();
super.visitLabel(end);
}
public void visitJumpInsn(int opcode, Label label) {
super.visitJumpInsn(opcode, label);
}
@Override
public void visitVarInsn(final int opcode, final int var){
super.visitVarInsn(opcode, var);;
}
...
}
[새로운 발견]
나는 지금 문제에 훨씬 더 가깝다고 생각한다.
- 인라인 방문자
MethodCallInliner
는 동일한 클래스를 가진이 방문자에 대한 또 다른 독립 테스트가 성공하므로 정확해야합니다. - 문제는 MethodVisitor 체인을 구축하는 방법에 있습니다. a) 방법 지침에서 한 번의 패스 만 방문하고 싶습니다. 2)
MethodCallInliner
체인의 끝에 배치됩니다. 그 전에는 더 많은 방문자가 추론 유형 정보에 삽입되어MethodCallInliner
.
내 체인 빌더는 다음과 같습니다.
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
.....
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new TransformationChain(Opcodes.ASM5, access, name, desc, signature, mv, _context);
//return new MethodCallInliner(access, desc, context); //This is OK.
}
public class TransformationChain extends BaseMethodTransform {
public TransformationChain(int api, int access, String name, String desc, String signature, MethodVisitor mv, ClassContext classContext) {
super(api, mv, classContext.getClassName(), name, desc);
....
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
_visitors.add(new AnalyzerAdapter(Opcodes.ASM5, owner, access, name,desc, cw.visitMethod(access, name, desc, owner, null)){
@Override
public void visitJumpInsn(final int opcode, final Label label){
super.visitJumpInsn(opcode, label);
}
});
MethodNode node = new MethodNode(access, name, desc, signature, null);
_visitors.add(node);
//cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
//MethodNode node = context.getClassContext().getMethodNode(name, desc);
//_visitors.add(new TypeInferencer(Opcodes.ASM5, cw.visitMethod(access, name, desc, null, null), node, context));
_visitors.add(name.equals(Constants.CONSTRUCTOR)?new ConstructorMerge(access, desc, context):
new MethodCallInliner(access, desc, context));
}
}
abstract class BaseMethodTransform extends MethodVisitor {
protected final List<MethodVisitor> _visitors = new LinkedList<MethodVisitor>();
public BaseMethodTransform(int api, MethodVisitor mv, String className, String methodName, String methodDesc) {
super(api, mv);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
for (MethodVisitor mv : _visitors) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
@Override
public void visitIntInsn(int opcode, int operand) {
for (MethodVisitor mv : _visitors) {
mv.visitIntInsn(opcode, operand);
}
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
for (MethodVisitor mv : _visitors) {
if (mv!= _visitors.get(_visitors.size()-1) || mv instanceof TraceMethodVisitor) {
continue;
}
mv.visitMaxs(maxStack, maxLocals);
}
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
for (MethodVisitor mv : _visitors) {
mv.visitJumpInsn(opcode, label);
}
}
......
}
여기서 내 발견 은 MethodVisitor가 여기에서 새로 생성 된 _visitors.add(new AnalyzerAdapter..);
에서 주석 처리하면 생성 된 클래스가 정확 TransformationChain
하다는 것입니다. 메서드의 일부 요소는 상태가있는 것으로 보이며, 이는 MethodWriters (모두 독립적 일지라도)에 의해 수정 될 수 있으며 이전 수정은 이후 방문자에게 영향을 미칩니다 .
나는 또한 그것이 레이블임을 알았습니다.
/**
* Informations about forward references. Each forward reference is
* described by two consecutive integers in this array: the first one is the
* position of the first byte of the bytecode instruction that contains the
* forward reference, while the second is the position of the first byte of
* the forward reference itself. In fact the sign of the first integer
* indicates if this reference uses 2 or 4 bytes, and its absolute value
* gives the position of the bytecode instruction. This array is also used
* as a bitset to store the subroutines to which a basic block belongs. This
* information is needed in {@linked MethodWriter#visitMaxs}, after all
* forward references have been resolved. Hence the same array can be used
* for both purposes without problems.
*/
private int[] srcAndRefPositions;
When it is first visited by AnalyzerAdapter::visitJmpAdadpter, two ints, e.g., 10 and 11, are inserted at the begin of the array. Then in the next iteration ``MethodCallInliner::visitJmpInsn`, another two new ints are added at position 2 and 3. Now the array content is:
[10, 11, 16, 17, 0, 0] in which the pair (10,11) is for AnalyzerAdapter and the pair (16,17) is for Method
MethodCallInliner
.
But what puzzles me here is: the ASM should be able to distinct different pairs for the right MethodVisitor when generating the bytcode class (or block, stack frame calculation whatever)?
The code can be accessed by https://github.com/xushijie/InlineMethod/tree/typeinference
The problem is caused when the label (the classreader reads from a class file) is visited by a MethodVisitor
pipeline. The label has a field int [] srcAndRefPositions
. Two of its consecutive positions (cfr. the end of my original post) are updated once the label is accessed by a MethodVisitor. In my case, the label in the ifeq label
holds 2 MethodVisitors. It seems the incorrect position in the srcAndRefPositions
is used when generating the class file (using the last MethodVisitor).
I did not investigate the root cause. Instead, my solution was to clone the label and then use the new label when it is visited by a MethodVisitor.
ReferenceURL : https://stackoverflow.com/questions/31998248/method-invocation-instruction-invokevirtual-invokestatic-is-substituted-by-som
'your programing' 카테고리의 다른 글
점이 선의 오른쪽인지 왼쪽인지 확인하는 방법 (0) | 2020.12.31 |
---|---|
PHP에서 HTML / XML을 어떻게 구문 분석하고 처리합니까? (0) | 2020.12.31 |
Qt 5.6을 사용하여 Android에서 NFC를 작동시키는 방법 (0) | 2020.12.31 |
레이어 지원 NSView 회전 및 왜곡 된 형제 (0) | 2020.12.31 |
페이징 라이브러리-페이지 및 크기를 가져 오는 API가있는 네트워크 + db에 대한 경계 콜백 (0) | 2020.12.31 |