Exemple #1
0
  private static CtMethod delegator0(CtMethod delegate, CtClass declaring)
      throws CannotCompileException, NotFoundException {
    MethodInfo deleInfo = delegate.getMethodInfo2();
    String methodName = deleInfo.getName();
    String desc = deleInfo.getDescriptor();
    ConstPool cp = declaring.getClassFile2().getConstPool();
    MethodInfo minfo = new MethodInfo(cp, methodName, desc);
    minfo.setAccessFlags(deleInfo.getAccessFlags());

    ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute();
    if (eattr != null) minfo.setExceptionsAttribute((ExceptionsAttribute) eattr.copy(cp, null));

    Bytecode code = new Bytecode(cp, 0, 0);
    boolean isStatic = Modifier.isStatic(delegate.getModifiers());
    CtClass deleClass = delegate.getDeclaringClass();
    CtClass[] params = delegate.getParameterTypes();
    int s;
    if (isStatic) {
      s = code.addLoadParameters(params, 0);
      code.addInvokestatic(deleClass, methodName, desc);
    } else {
      code.addLoad(0, deleClass);
      s = code.addLoadParameters(params, 1);
      code.addInvokespecial(deleClass, methodName, desc);
    }

    code.addReturn(delegate.getReturnType());
    code.setMaxLocals(++s);
    code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
    minfo.setCodeAttribute(code.toCodeAttribute());
    return new CtMethod(minfo, declaring);
  }
  private CodeCoverage(ClassLoader classLoader, MethodExclusion exclusion, String... classes)
      throws Exception {
    for (String clazz : classes) {
      Map<Tuple, Set<CodeLine>> map = new TreeMap<>();
      report.put(clazz, map);

      List<Tuple> mds = new ArrayList<>();
      descriptors.put(clazz, mds);

      InputStream is = classLoader.getResourceAsStream(clazz.replace(".", "/") + ".class");
      ClassFile classFile = getClassFile(clazz, is);

      List<MethodInfo> methods = classFile.getMethods();
      for (MethodInfo m : methods) {
        if (exclusion.exclude(classFile, m) == false) {
          String descriptor = m.getDescriptor();
          Tuple tuple = new Tuple(m.getName(), descriptor);
          map.put(tuple, new TreeSet<CodeLine>());
          mds.add(tuple);
        }
      }

      if (isAccessFlagSet(classFile.getAccessFlags(), AccessFlag.ANNOTATION)) {
        boolean hasAttributes = methods.size() > 0;
        annotations.put(clazz, hasAttributes);
        if (hasAttributes == false) {
          map.put(TYPE_USAGE, new TreeSet<CodeLine>());
          mds.add(TYPE_USAGE);
        }
      } else {
        fillSupers(classFile);
      }
    }
  }
  public BaseClassData(ClassFile file, ClassLoader loader, boolean replaceable) {
    className = file.getName();
    this.replaceable = replaceable;
    internalName = Descriptor.toJvmName(file.getName());
    this.loader = loader;
    superClassName = file.getSuperclass();
    boolean finalMethod = false;
    Set<MethodData> meths = new HashSet<MethodData>();
    for (Object o : file.getMethods()) {
      String methodClassName = className;
      MethodInfo m = (MethodInfo) o;
      MemberType type = MemberType.NORMAL;
      if ((m.getDescriptor().equals(Constants.ADDED_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_STATIC_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_CONSTRUCTOR_DESCRIPTOR))) {
        type = MemberType.ADDED_SYSTEM;
      } else if (m.getAttribute(Constants.FINAL_METHOD_ATTRIBUTE) != null) {
        finalMethod = true;
      }

      MethodData md =
          new MethodData(
              m.getName(),
              m.getDescriptor(),
              methodClassName,
              type,
              m.getAccessFlags(),
              finalMethod);
      meths.add(md);
    }
    this.methods = Collections.unmodifiableSet(meths);
    Set<FieldData> fieldData = new HashSet<FieldData>();
    for (Object o : file.getFields()) {
      FieldInfo m = (FieldInfo) o;
      MemberType mt = MemberType.NORMAL;
      fieldData.add(new FieldData(m, mt, className, m.getAccessFlags()));
    }
    this.fields = Collections.unmodifiableSet(fieldData);
  }
  protected void enhanceAttributesAccess(
      CtClass managedCtClass,
      IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement and abstract methods (methods without any code)
      if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          final int index = itr.next();
          final int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }
          final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          final PersistentAttributeAccessMethods attributeMethods =
              attributeDescriptorMap.get(fieldName);

          // its not a field we have enhanced for interception, so skip it
          if (attributeMethods == null) {
            continue;
          }
          // System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName,
          // methodName );
          log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName);

          if (op == Opcode.GETFIELD) {
            final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getReader());
            itr.writeByte(Opcode.INVOKESPECIAL, index);
            itr.write16bit(methodIndex, index + 1);
          } else {
            final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getWriter());
            itr.writeByte(Opcode.INVOKESPECIAL, index);
            itr.write16bit(methodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, bb);
      }
    }
  }
  protected void enhanceAttributesAccess(Map<String, CtField> fieldsMap, CtClass managedCtClass)
      throws Exception {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
    final ClassPool classPool = managedCtClass.getClassPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement, and abstract methods (methods without any code)
      if (methodName.startsWith(DROOLS_PREFIX) || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          final int index = itr.next();
          final int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }

          final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          CtField ctField = fieldsMap.get(fieldName);
          if (ctField == null) {
            continue;
          }

          // if we are in constructors, only need to intercept assignment statement for Reactive
          // Collection/List/... (regardless they may be final)
          if (methodInfo.isConstructor() && !(isCtFieldACollection(ctField))) {
            continue;
          }

          if (op == Opcode.PUTFIELD) {
            // addMethod is a safe add, if constant already present it return the existing value
            // without adding.
            final int methodIndex = addMethod(constPool, writeMethods.get(fieldName));
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(methodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new Exception(msg, bb);
      }
    }
  }
  /**
   * add a bogus constructor call to a bytecode sequence so a constructor can pass bytecode
   * validation
   *
   * @param bytecode
   */
  public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) {
    MethodInfo constructorToCall = null;
    for (Object meth : file.getMethods()) {
      MethodInfo m = (MethodInfo) meth;
      if (m.getName().equals("<init>")) {
        constructorToCall = m;
        break;
      }
    }
    if (constructorToCall == null) {
      return false;
    }
    // push this onto the stack
    code.add(Bytecode.ALOAD_0);

    String[] params =
        DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor());
    for (String p : params) {
      // int char short boolean byte
      if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) {
        // push integer 0
        code.add(Opcode.ICONST_0);
      }
      // long
      else if (p.equals("J")) {
        code.add(Opcode.LCONST_0);
      }
      // double
      else if (p.equals("D")) {
        code.add(Opcode.DCONST_0);
      }
      // float
      else if (p.equals("F")) {
        code.add(Opcode.FCONST_0);
      }
      // arrays and reference types
      else {
        code.add(Opcode.ACONST_NULL);
      }
    }
    // all our args should be pushed onto the stack, call the constructor
    code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor());
    code.add(Opcode.RETURN);
    return true;
  }
  @Override
  public boolean transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      ClassFile file)
      throws IllegalClassFormatException, BadBytecode {

    /**
     * Hack up the proxy factory so it stores the proxy ClassFile. We need this to regenerate
     * proxies.
     */
    if (file.getName().equals("org.jboss.weld.bean.proxy.ProxyFactory")) {
      for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) {
        if (method.getName().equals("createProxyClass")) {
          final MethodInvokationManipulator methodInvokationManipulator =
              new MethodInvokationManipulator();
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              ClassLoader.class.getName(),
              WeldProxyClassLoadingDelegate.class.getName(),
              "loadClass",
              "(Ljava/lang/String;)Ljava/lang/Class;",
              "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
              loader);
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              "org.jboss.weld.util.bytecode.ClassFileUtils",
              WeldProxyClassLoadingDelegate.class.getName(),
              "toClass",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              loader);
          HashSet<MethodInfo> modifiedMethods = new HashSet<MethodInfo>();
          methodInvokationManipulator.transformClass(file, loader, true, modifiedMethods);
          for (MethodInfo m : modifiedMethods) {
            m.rebuildStackMap(ClassPool.getDefault());
          }
          return true;
        } else if (method.getName().equals("<init>")) {

          Integer beanArgument = null;
          int count = 0;
          for (final String paramType :
              DescriptorUtils.descriptorStringToParameterArray(method.getDescriptor())) {
            if (paramType.equals("javax/enterprise/inject/spi/Bean")) {
              beanArgument = count;
              break;
            } else if (paramType.equals("D") || paramType.equals("J")) {
              count += 2;
            } else {
              count++;
            }
          }
          if (beanArgument == null) {
            log.error(
                "Constructor org.jboss.weld.bean.proxy.ProxyFactory.<init>"
                    + method.getDescriptor()
                    + " does not have a bean parameter, proxies produced by this factory will not be reloadable");
            continue;
          }

          // similar to other tracked instances
          // but we need a strong ref
          Bytecode code = new Bytecode(file.getConstPool());
          code.addAload(0);
          code.addAload(beanArgument);
          code.addInvokestatic(
              WeldClassChangeAware.class.getName(),
              "addProxyFactory",
              "(Lorg/jboss/weld/bean/proxy/ProxyFactory;)V");
          CodeIterator it = method.getCodeAttribute().iterator();
          it.skipConstructor();
          it.insert(code.get());
        }
      }
    }
    return false;
  }
  /**
   * Replace access to fields of entities (for example, entity.field) with a call to the enhanced
   * getter / setter (in this example, entity.$$_hibernate_read_field()). It's assumed that the
   * target entity is enhanced as well.
   *
   * @param managedCtClass Class to enhance
   */
  public void enhanceFieldAccess(CtClass managedCtClass) {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement and abstract methods (methods without any code)
      if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          int index = itr.next();
          int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }
          String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          String fieldClassName =
              constPool.getClassInfo(constPool.getFieldrefClass(itr.u16bitAt(index + 1)));
          CtClass targetCtClass = this.classPool.getCtClass(fieldClassName);

          if (!enhancementContext.isEntityClass(targetCtClass)
              && !enhancementContext.isCompositeClass(targetCtClass)) {
            continue;
          }
          if (targetCtClass == managedCtClass
              || !enhancementContext.isPersistentField(targetCtClass.getField(fieldName))
              || "this$0".equals(fieldName)) {
            continue;
          }

          log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName);

          if (op == Opcode.GETFIELD) {
            int fieldReaderMethodIndex =
                constPool.addMethodrefInfo(
                    constPool.addClassInfo(fieldClassName),
                    EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName,
                    "()" + constPool.getFieldrefType(itr.u16bitAt(index + 1)));
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(fieldReaderMethodIndex, index + 1);
          } else {
            int fieldWriterMethodIndex =
                constPool.addMethodrefInfo(
                    constPool.addClassInfo(fieldClassName),
                    EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName,
                    "(" + constPool.getFieldrefType(itr.u16bitAt(index + 1)) + ")V");
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(fieldWriterMethodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, bb);
      } catch (NotFoundException nfe) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, nfe);
      }
    }
  }
 public boolean match(String filename, ClassFile classFile, ClassMap tempClassMap) {
   for (Object o : classFile.getMethods()) {
     MethodInfo methodInfo = (MethodInfo) o;
     classMod.methodInfo = methodInfo;
     CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
     if (codeAttribute == null) {
       continue;
     }
     if (getClassMap().hasMap(deobfMethod)) {
       MethodRef obfTarget = (MethodRef) getClassMap().map(deobfMethod);
       if (!methodInfo.getName().equals(obfTarget.getName())) {
         continue;
       }
     }
     ArrayList<String> deobfTypes = null;
     ArrayList<String> obfTypes = null;
     if (deobfMethod != null && deobfMethod.getType() != null) {
       deobfTypes = ConstPoolUtils.parseDescriptor(deobfMethod.getType());
       obfTypes = ConstPoolUtils.parseDescriptor(methodInfo.getDescriptor());
       if (!isPotentialTypeMatch(deobfTypes, obfTypes)) {
         continue;
       }
     }
     ConstPool constPool = methodInfo.getConstPool();
     CodeIterator codeIterator = codeAttribute.iterator();
     initMatcher();
     ArrayList<JavaRef> tempMappings = new ArrayList<JavaRef>();
     try {
       match:
       for (int offset = 0;
           offset < codeIterator.getCodeLength() && matcher.match(methodInfo, offset);
           offset = codeIterator.next()) {
         tempMappings.clear();
         for (Map.Entry<Integer, JavaRef> entry : xrefs.entrySet()) {
           int captureGroup = entry.getKey();
           JavaRef xref = entry.getValue();
           byte[] code = matcher.getCaptureGroup(captureGroup);
           int index = Util.demarshal(code, 1, 2);
           ConstPoolUtils.matchOpcodeToRefType(code[0], xref);
           ConstPoolUtils.matchConstPoolTagToRefType(constPool.getTag(index), xref);
           JavaRef newRef = ConstPoolUtils.getRefForIndex(constPool, index);
           if (!isPotentialTypeMatch(xref.getType(), newRef.getType())) {
             if (deobfMethod != null) {
               Logger.log(
                   Logger.LOG_METHOD,
                   "method %s %s matches %s %s, but",
                   methodInfo.getName(),
                   methodInfo.getDescriptor(),
                   deobfMethod.getName(),
                   deobfMethod.getType());
             }
             Logger.log(
                 Logger.LOG_METHOD,
                 "method %s %s failed xref #%d %s %s -> %s %s",
                 methodInfo.getName(),
                 methodInfo.getDescriptor(),
                 captureGroup,
                 xref.getName(),
                 xref.getType(),
                 newRef.getName(),
                 newRef.getType());
             continue match;
           }
           tempMappings.add(xref);
           tempMappings.add(newRef);
         }
         for (int i = 0; i + 1 < tempMappings.size(); i += 2) {
           tempClassMap.addMap(tempMappings.get(i), tempMappings.get(i + 1));
         }
         if (deobfMethod != null) {
           String deobfName = classMod.getDeobfClass();
           tempClassMap.addClassMap(deobfName, ClassMap.filenameToClassName(filename));
           tempClassMap.addMethodMap(
               deobfName, deobfMethod.getName(), methodInfo.getName(), methodInfo.getDescriptor());
           if (deobfTypes != null && obfTypes != null) {
             for (int i = 0; i < deobfTypes.size(); i++) {
               String desc = ClassMap.descriptorToClassName(deobfTypes.get(i));
               String obf = ClassMap.descriptorToClassName(obfTypes.get(i));
               if (!obf.equals(desc)) {
                 tempClassMap.addClassMap(desc, obf);
               }
             }
           }
         }
         afterMatch(classFile, methodInfo);
         classMod.methodInfo = null;
         return true;
       }
     } catch (BadBytecode e) {
       Logger.log(e);
     }
   }
   classMod.methodInfo = null;
   return false;
 }
  protected void checkClassFile(ClassFile file) throws Exception {
    Map<Integer, Triple> calls = new HashMap<>();

    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
      // we have a method call
      BytecodeUtils.Ref ref = BytecodeUtils.getRef(pool, i);
      String className = ref.getClassName(pool, i);
      if (className != null) {
        String methodName = ref.getName(pool, i);
        String methodDesc = ref.getDesc(pool, i);
        fillCalls(i, className, methodName, methodDesc, calls);
      }
    }

    if (calls.isEmpty() && annotations.isEmpty()) {
      return;
    }

    String className = file.getName();

    AnnotationsAttribute faa =
        (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.visibleTag);
    checkAnnotations(className, TYPE_USAGE.getMethodName(), faa, -1);

    List<MethodInfo> methods = file.getMethods();
    for (MethodInfo m : methods) {
      try {
        // ignore abstract methods
        if (m.getCodeAttribute() == null) {
          continue;
        }

        AnnotationsAttribute maa =
            (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag);
        boolean annotationsChecked = false;
        int firstLine = -1;

        CodeIterator it = m.getCodeAttribute().iterator();
        while (it.hasNext()) {
          // loop through the bytecode
          final int index = it.next();
          final int line = m.getLineNumber(index);

          if (annotationsChecked == false) {
            annotationsChecked = true;
            firstLine = line;
            checkAnnotations(
                className, m.getName(), maa, line - 2); // -2 to get the line above the method
          }

          int op = it.byteAt(index);
          // if the bytecode is a method invocation
          if (op == CodeIterator.INVOKEVIRTUAL
              || op == CodeIterator.INVOKESTATIC
              || op == CodeIterator.INVOKEINTERFACE
              || op == CodeIterator.INVOKESPECIAL) {
            int val = it.s16bitAt(index + 1);
            Triple triple = calls.get(val);
            if (triple != null) {
              Map<Tuple, Set<CodeLine>> map = report.get(triple.className);
              Set<CodeLine> set = map.get(triple.tuple);
              CodeLine cl = new CodeLine(className, m.getName(), line);
              set.add(cl.modify()); // check for .jsp, etc
            }
          }
        }

        if (BaseMethodExclusion.isBridge(m) == false) {
          SignatureAttribute.MethodSignature signature =
              SignatureAttribute.toMethodSignature(m.getDescriptor());
          handleMethodSignature(className, m.getName(), firstLine - 1, signature.getReturnType());
          handleMethodSignature(
              className, m.getName(), firstLine - 1, signature.getParameterTypes());
          handleMethodSignature(
              className, m.getName(), firstLine - 1, signature.getExceptionTypes());
        }

        ParameterAnnotationsAttribute paa =
            (ParameterAnnotationsAttribute)
                m.getAttribute(ParameterAnnotationsAttribute.visibleTag);
        if (paa != null) {
          Annotation[][] paas = paa.getAnnotations();
          if (paas != null) {
            for (Annotation[] params : paas) {
              for (Annotation a : params) {
                for (Map.Entry<String, Boolean> entry : annotations.entrySet()) {
                  if (entry.getKey().equals(a.getTypeName())) {
                    checkAnnotation(
                        className, m.getName(), firstLine - 1, entry.getValue(), entry.getKey(), a);
                  }
                }
              }
            }
          }
        }

        m.getCodeAttribute().computeMaxStack();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }