如何重用asm中methodNode的原始帧信息来创建`org.objectweb.asm.tree.analysis.Frame`

时间:2015-05-21 03:04:28

标签: java bytecode java-bytecode-asm

如何仅使用org.objectweb.asm.tree.analysis.Frame中的FrameNodesLocalVariableNodes为方法中的每条指令构建MethodNode

上下文

在检测一些代码时,我需要一些原始指令的所有本地和堆栈类型。

目前我这样做:

Analyzer analyzer = new Analyzer(new MyBasicInterpreter());
Frame[] frames = analyzer.analyze(ownerClass, methodNode);

这为Frame中的每条指令提供了methodNode

但是,拥有确切类型意味着正确实现BasicInterpreter.merge并且需要为任意类型解析公共超类。这很快升级为必须知道一堆类的超类和接口(即必须从类加载器中读取更多信息)。

所以我想知道我是否可以避免使用Analyzer并只使用原始帧信息来重建我需要的数据。

原始类总是jdk 1.8.0类并且具有帧信息。 我真的需要知道堆栈中的类型。

1 个答案:

答案 0 :(得分:1)

您无法完全避免分析,因为StackMapTable仅包含有关分支目标和合并点的信息。但是,对于线性流,BasicInterpreter已包含必要的操作,如果从StackMapTable中提取结果信息,则确实不需要实现合并操作。

唯一的缺点是你必须重新实现方法条目初始帧的构造,因为该操作被隐藏在Analyzer内,并且ASM似乎没有为隐式堆栈映射框提供节点正式存在于方法的开头。

以下是完整代码:

static List<Frame<BasicValue>> analyze(
    String ownerClass, MethodNode methodNode) throws AnalyzerException {

  final BasicValue ownerType=toBasicValue(null, ownerClass);
  BasicInterpreter interpreter = new BasicInterpreter() {
    @Override
    public BasicValue newValue(Type type) {
      return type==null || isPrimitive(type.getSort())? super.newValue(type):
             type.equals(ownerType.getType())? ownerType: new BasicValue(type);
    }
    private boolean isPrimitive(int sort) {
      return sort!=Type.OBJECT&&sort!=Type.ARRAY;
    }
  };
  List<Frame<BasicValue>> frames=new ArrayList<>();

  Frame<BasicValue> current = methodEntryFrame(methodNode, ownerType, interpreter);

  for(int ix=0, num=methodNode.instructions.size(); ix<num; ix++) {

    AbstractInsnNode i=methodNode.instructions.get(ix);

    if(i.getType() == AbstractInsnNode.FRAME) {
      current = extractFrame(methodNode, ownerType, (FrameNode)i);
      continue;
    } else if(i.getOpcode()<0) continue;//pseudo instruction node
    frames.add(new Frame<>(current));
    current.execute(i, interpreter);
  }

  return frames;
}

private static Frame<BasicValue> extractFrame(
    MethodNode methodNode, BasicValue ownerType, FrameNode fn) {

  Frame<BasicValue> current = new Frame<>(methodNode.maxLocals, methodNode.maxStack);
  int locals = fn.local!=null? fn.local.size(): 0;
  for(int lIx=0, lCount=locals; lIx<lCount; lIx++)
    current.setLocal(lIx, toBasicValue(ownerType, fn.local.get(lIx)));
  for(int lIx=locals; lIx<methodNode.maxLocals; lIx++)
    current.setLocal(lIx, BasicValue.UNINITIALIZED_VALUE);
  if(fn.stack!=null)
    for(Object obj: fn.stack) current.push(toBasicValue(ownerType, obj));
  return current;
}

private static Frame<BasicValue> methodEntryFrame(
    MethodNode methodNode, BasicValue ownerType, BasicInterpreter interpreter) {

  Frame<BasicValue> current = new Frame<>(methodNode.maxLocals, methodNode.maxStack);
  current.setReturn(interpreter.newValue(Type.getReturnType(methodNode.desc)));
  Type[] args = Type.getArgumentTypes(methodNode.desc);
  int local = 0;
  if((methodNode.access & Opcodes.ACC_STATIC) == 0)
    current.setLocal(local++, ownerType);
  for(int ix = 0; ix < args.length; ix++) {
    Type type = args[ix];
    current.setLocal(local++, interpreter.newValue(type));
    if(type.getSize() == 2)
      current.setLocal(local++, BasicValue.UNINITIALIZED_VALUE);
  }
  while(local < methodNode.maxLocals)
    current.setLocal(local++, BasicValue.UNINITIALIZED_VALUE);
  return current;
}

private static BasicValue toBasicValue(BasicValue owner, Object object) {
  if(object instanceof String) return refType(owner,(String)object);
  if(object instanceof Integer)
    switch((Integer)object) {
      case 0: return BasicValue.UNINITIALIZED_VALUE;
      case 1: return BasicValue.INT_VALUE;
      case 2: return BasicValue.FLOAT_VALUE;
      case 3: return BasicValue.DOUBLE_VALUE;
      case 4: return BasicValue.LONG_VALUE;
      case 5: return BasicValue.REFERENCE_VALUE;// null
      case 6: return owner;// uninitialized_this
      default: throw new IllegalStateException();
  }
  // uninitialized object, object is a LabelNode pointing to the ANEW instruction
  return BasicValue.REFERENCE_VALUE;
}

static final BasicValue OBJECT = new BasicValue(Type.getObjectType("java/lang/Object"));

private static BasicValue refType(BasicValue owner, String name) {
  if(name.equals("java/lang/Object")) return OBJECT;
  if(owner!=null && owner.getType().getInternalName().equals(name))
    return owner;
  return new BasicValue(Type.getObjectType(name));
}