Package com.dragome.callbackevictor.serverside.bytecode.transformation.asm

Source Code of com.dragome.callbackevictor.serverside.bytecode.transformation.asm.ContinuationMethodAnalyzer$MyVariables

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dragome.callbackevictor.serverside.bytecode.transformation.asm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
import org.objectweb.asm.tree.analysis.SourceInterpreter;
import org.objectweb.asm.tree.analysis.SourceValue;

import com.dragome.commons.ContinueReflection;

public class ContinuationMethodAnalyzer extends MethodNode implements Opcodes
{

  public class MyVerifier extends SimpleVerifier
  {
    MyVerifier()
    {
      if (_useLoader != null)
      {
        setClassLoader(_useLoader);
        //Thread.currentThread().setContextClassLoader(_useLoader);
      }
    }

    protected Class<?> getClass(Type t)
    {
      try
      {
        if (t.getSort() == Type.ARRAY)
        {
          return Class.forName(t.getDescriptor().replace('/', '.'), true, Thread.currentThread().getContextClassLoader());
        }
        return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader());
        //return Class.forName(t.getClassName()); //for now try this. It has to use the current classloader
      }
      catch (ClassNotFoundException e)
      {
        throw new RuntimeException(e.toString());
      }
    }

    /*
     * protected Class<?> getClass(Type t) {
        try {
            System.out.println("Resolving " + t.getClassName() + " using classloader: " + Thread.currentThread().getContextClassLoader());
            if (t.getSort() == Type.ARRAY) {
                return Class.forName(t.getDescriptor().replace('/', '.'), true, Thread.currentThread().getContextClassLoader());
            }
            //return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader());
            return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader()); //for now try this. It has to use the current classloader
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e.toString());
        }
    }*/
  }

  public class MyVariables
  {
    private int _methodObject;
    private int _object;
    private int _args;

    MyVariables(int m, int o, int a)
    {
      _methodObject= m;
      _object= o;
      _args= a;
    }

    public int methodVar()
    {
      return _methodObject;
    }
    public int objectVar()
    {
      return _object;
    }
    public int argsVar()
    {
      return _args;
    }
  }

  protected final String className;
  protected final ClassVisitor cv;
  protected final MethodVisitor mv;

  protected final List<Label> labels= new ArrayList<Label>();
  protected final List<MethodInsnNode> nodes= new ArrayList<MethodInsnNode>();
  protected final List<MethodInsnNode> methods= new ArrayList<MethodInsnNode>();

  //RS:
  private List _variables;
  protected final Map<MethodInsnNode, MyVariables> _reflectMapping= new HashMap<MethodInsnNode, MyVariables>();
  protected boolean _continueReflection;
  //RS:

  protected Analyzer analyzer;
  public int stackRecorderVar;

  private ClassLoader _useLoader;

  public ContinuationMethodAnalyzer(String className, ClassVisitor cv, MethodVisitor mv, int access, String name, String desc, String signature, String[] exceptions)
  {
    this(className, cv, mv, access, name, desc, signature, exceptions, null);
  }
  public ContinuationMethodAnalyzer(String className, ClassVisitor cv, MethodVisitor mv, int access, String name, String desc, String signature, String[] exceptions, ClassLoader loader)
  {
    super(Opcodes.ASM5, access, name, desc, signature, exceptions);
    this.className= className;
    this.cv= cv;
    this.mv= mv;
    _useLoader= loader;

    //RS:
    this._variables= new ArrayList();
    _continueReflection= false;
  }

  public int getIndex(AbstractInsnNode node)
  {
    return instructions.indexOf(node);
  }

  //RS:
  public void visitVarInsn(int opcode, int var)
  {
    super.visitVarInsn(opcode, var);
    if (opcode == ALOAD)
    {
      //store it for reuse for Method.invoke
      _variables.add(var);
    }
  }

  public AnnotationVisitor visitAnnotation(String desc, boolean visible)
  {
    String chk= Type.getDescriptor(ContinueReflection.class);
    if (desc.startsWith(chk))
      _continueReflection= true;

    return super.visitAnnotation(desc, visible);
  }

  public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)
  {
    MethodInsnNode mnode= new MethodInsnNode(opcode, owner, name, desc, itf);
    if (opcode == INVOKESPECIAL || "<init>".equals(name))
    {
      methods.add(mnode);
    }
    if (needsFrameGuard(opcode, owner, name, desc) /* && transformer.inScope(owner, name)*/)
    {
      Label label= new Label();
      super.visitLabel(label);
      LabelNode labelNode= new LabelNode(label);
      instructions.add(labelNode);
      labels.add(label);
      nodes.add(mnode);

      //RS:
      if (_continueReflection && name.equals("invoke") && owner.startsWith("java/lang/reflect/Method"))
      {
        int mthd= ((Integer) _variables.get(_variables.size() - 3)).intValue();
        int obj= ((Integer) _variables.get(_variables.size() - 2)).intValue();
        int args= ((Integer) _variables.get(_variables.size() - 1)).intValue();
        MyVariables vars= new MyVariables(mthd, obj, args);
        _reflectMapping.put(mnode, vars);
      }
      //RS:
    }
    instructions.add(mnode);
  }

  public void visitEnd()
  {
    if (instructions.size() == 0 || labels.size() == 0)
    {
      accept(mv);
      return;
    }

    /*
    {
              TraceMethodVisitor mv = new TraceMethodVisitor();
              System.err.println(name + desc);
              for (int j = 0; j < instructions.size(); ++j) {
                  ((AbstractInsnNode) instructions.get(j)).accept(mv);
                  System.err.print("   " + mv.text.get(j)); // mv.text.get(j));
              }
              System.err.println();
    }
    */

    this.stackRecorderVar= maxLocals;
    try
    {
      moveNew();

      // analyzer = new Analyzer(new BasicVerifier());
      /*
       * analyzer = new Analyzer(new SimpleVerifier() {

          protected Class<?> getClass(Type t) {
              try {
                  ClassLoader loader = Thread.currentThread().getContextClassLoader();
                  if (_useLoader != null)
                      loader = _useLoader;
                  if (t.getSort() == Type.ARRAY) {
                      return Class.forName(t.getDescriptor().replace('/', '.'), true, loader);
                  }
                  //return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader());
                  return Class.forName(t.getClassName(), loader); //for now try this. It has to use the current classloader
              } catch (ClassNotFoundException e) {
                  throw new RuntimeException(e.toString());
              }
          }


      })*/
      analyzer= new Analyzer(new MyVerifier())
      {

        protected Frame newFrame(final int nLocals, final int nStack)
        {
          return new MonitoringFrame(nLocals, nStack);
        }

        protected Frame newFrame(final Frame src)
        {
          return new MonitoringFrame(src);
        }

        public Frame[] analyze(final String owner, final MethodNode m) throws AnalyzerException
        {
          // System.out.println("Analyze: "+owner+"|"+m.name+"|"+m.signature+"|"+m.tryCatchBlocks);
          final Frame[] frames= super.analyze(owner, m);
          for (int i= 0; i < m.instructions.size(); i++)
          {
            int opcode= m.instructions.get(i).getOpcode();
            if (opcode == MONITORENTER || opcode == MONITOREXIT)
            {
              // System.out.println(i);
            }
          }
          return frames;
        }
      };

      analyzer.analyze(className, this);
      accept(new ContinuationMethodAdapter(this));

      /*
      {
                TraceMethodVisitor mv = new TraceMethodVisitor();
                System.err.println("=================");

                System.err.println(name + desc);
                for (int j = 0; j < instructions.size(); ++j) {
                    ((AbstractInsnNode) instructions.get(j)).accept(mv);
                    System.err.print("   " + mv.text.get(j)); // mv.text.get(j));
                }
                System.err.println();
      }
      */

    }
    catch (AnalyzerException ex)
    {
      // TODO log the error or fail?
      ex.printStackTrace();
      accept(mv);

    }
  }

  @SuppressWarnings("unchecked")
  void moveNew() throws AnalyzerException
  {
    SourceInterpreter i= new SourceInterpreter();
    Analyzer a= new Analyzer(i);
    a.analyze(className, this);

    final HashMap<AbstractInsnNode, MethodInsnNode> movable= new HashMap<AbstractInsnNode, MethodInsnNode>();

    Frame[] frames= a.getFrames();
    for (int j= 0; j < methods.size(); j++)
    {
      MethodInsnNode mnode= methods.get(j);
      // require to move NEW instruction
      int n= instructions.indexOf(mnode);
      Frame f= frames[n];
      Type[] args= Type.getArgumentTypes(mnode.desc);

      SourceValue v= (SourceValue) f.getStack(f.getStackSize() - args.length - 1);
      Set<AbstractInsnNode> insns= v.insns;
      for (final AbstractInsnNode ins : insns)
      {
        if (ins.getOpcode() == NEW)
        {
          movable.put(ins, mnode);
        }
        else
        {
          // other known patterns
          int n1= instructions.indexOf(ins);
          if (ins.getOpcode() == DUP)
          { // <init> with params
            AbstractInsnNode ins1= instructions.get(n1 - 1);
            if (ins1.getOpcode() == NEW)
            {
              movable.put(ins1, mnode);
            }
          }
          else if (ins.getOpcode() == SWAP)
          { // in exception handler
            AbstractInsnNode ins1= instructions.get(n1 - 1);
            AbstractInsnNode ins2= instructions.get(n1 - 2);
            if (ins1.getOpcode() == DUP_X1 && ins2.getOpcode() == NEW)
            {
              movable.put(ins2, mnode);
            }
          }
        }
      }
    }

    int updateMaxStack= 0;
    for (final Map.Entry<AbstractInsnNode, MethodInsnNode> e : movable.entrySet())
    {
      AbstractInsnNode node1= e.getKey();
      int n1= instructions.indexOf(node1);
      AbstractInsnNode node2= instructions.get(n1 + 1);
      AbstractInsnNode node3= instructions.get(n1 + 2);
      int producer= node2.getOpcode();

      instructions.remove(node1); // NEW
      boolean requireDup= false;
      if (producer == DUP)
      {
        instructions.remove(node2); // DUP
        requireDup= true;
      }
      else if (producer == DUP_X1)
      {
        instructions.remove(node2); // DUP_X1
        instructions.remove(node3); // SWAP
        requireDup= true;
      }

      MethodInsnNode mnode= (MethodInsnNode) e.getValue();
      AbstractInsnNode nm= mnode;

      int varOffset= stackRecorderVar + 1;
      Type[] args= Type.getArgumentTypes(mnode.desc);

      // optimizations for some common cases
      if (args.length == 0)
      {
        final InsnList doNew= new InsnList();
        doNew.add(node1); // NEW
        if (requireDup)
          doNew.add(new InsnNode(DUP));
        instructions.insertBefore(nm, doNew);
        nm= doNew.getLast();
        continue;
      }

      if (args.length == 1 && args[0].getSize() == 1)
      {
        final InsnList doNew= new InsnList();
        doNew.add(node1); // NEW
        if (requireDup)
        {
          doNew.add(new InsnNode(DUP));
          doNew.add(new InsnNode(DUP2_X1));
          doNew.add(new InsnNode(POP2));
          updateMaxStack= updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
        }
        else
          doNew.add(new InsnNode(SWAP));
        instructions.insertBefore(nm, doNew);
        nm= doNew.getLast();
        continue;
      }

      // TODO this one untested!
      if ((args.length == 1 && args[0].getSize() == 2) || (args.length == 2 && args[0].getSize() == 1 && args[1].getSize() == 1))
      {
        final InsnList doNew= new InsnList();
        doNew.add(node1); // NEW
        if (requireDup)
        {
          doNew.add(new InsnNode(DUP));
          doNew.add(new InsnNode(DUP2_X2));
          doNew.add(new InsnNode(POP2));
          updateMaxStack= updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
        }
        else
        {
          doNew.add(new InsnNode(DUP_X2));
          doNew.add(new InsnNode(POP));
          updateMaxStack= updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for temp value
        }
        instructions.insertBefore(nm, doNew);
        nm= doNew.getLast();
        continue;
      }

      final InsnList doNew= new InsnList();
      // generic code using temporary locals
      // save stack
      for (int j= args.length - 1; j >= 0; j--)
      {
        Type type= args[j];

        doNew.add(new VarInsnNode(type.getOpcode(ISTORE), varOffset));
        varOffset+= type.getSize();
      }
      if (varOffset > maxLocals)
      {
        maxLocals= varOffset;
      }

      doNew.add(node1); // NEW

      if (requireDup)
        doNew.add(new InsnNode(DUP));

      // restore stack
      for (int j= 0; j < args.length; j++)
      {
        Type type= args[j];
        varOffset-= type.getSize();

        doNew.add(new VarInsnNode(type.getOpcode(ILOAD), varOffset));

        // clean up store to avoid memory leak?
        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY)
        {
          updateMaxStack= updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for ACONST_NULL

          doNew.add(new InsnNode(ACONST_NULL));

          doNew.add(new VarInsnNode(type.getOpcode(ISTORE), varOffset));
        }
      }
      instructions.insertBefore(nm, doNew);
      nm= doNew.getLast();
    }

    maxStack+= updateMaxStack;
  }

  boolean needsFrameGuard(int opcode, String owner, String name, String desc)
  {
    /* TODO: need to customize a way enchancer skips classes/methods
        if (owner.startsWith("java/")) {
          System.out.println("SKIP:: " + owner + "." + name + desc);
          return false;
        }
    */

    if (opcode == Opcodes.INVOKEINTERFACE || (opcode == Opcodes.INVOKESPECIAL && !"<init>".equals(name)) || opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKEVIRTUAL)
    {
      return true;
    }
    return false;
  }

}
TOP

Related Classes of com.dragome.callbackevictor.serverside.bytecode.transformation.asm.ContinuationMethodAnalyzer$MyVariables

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.