private CtMethod generateFieldReader(
      CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) {
    final String fieldName = persistentField.getName();
    final String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;

    // read attempts only have to deal lazy-loading support, not dirty checking;
    // so if the field is not enabled as lazy-loadable return a plain simple getter as the reader
    if (!enhancementContext.isLazyLoadable(persistentField)) {
      return MethodWriter.addGetter(managedCtClass, fieldName, readerName);
    }

    try {
      return MethodWriter.write(
          managedCtClass,
          "public %s %s() {%n  %s%n  return this.%s;%n}",
          persistentField.getType().getName(),
          readerName,
          typeDescriptor.buildReadInterceptionBodyFragment(fieldName),
          fieldName);
    } catch (CannotCompileException cce) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field reader method [%s]",
              managedCtClass.getName(), readerName);
      throw new EnhancementException(msg, cce);
    } catch (NotFoundException nfe) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field reader method [%s]",
              managedCtClass.getName(), readerName);
      throw new EnhancementException(msg, nfe);
    }
  }
  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);
      }
    }
  }
  private CtMethod generateFieldWriter(
      CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) {
    final String fieldName = persistentField.getName();
    final String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;

    try {
      final CtMethod writer;

      if (!enhancementContext.isLazyLoadable(persistentField)) {
        writer = MethodWriter.addSetter(managedCtClass, fieldName, writerName);
      } else {
        writer =
            MethodWriter.write(
                managedCtClass,
                "public void %s(%s %s) {%n  %s%n}",
                writerName,
                persistentField.getType().getName(),
                fieldName,
                typeDescriptor.buildWriteInterceptionBodyFragment(fieldName));
      }

      if (enhancementContext.isCompositeClass(managedCtClass)) {
        writer.insertBefore(
            String.format(
                "if (%s != null) { %<s.callOwner(\".%s\"); }%n",
                EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME, fieldName));
      } else if (enhancementContext.doDirtyCheckingInline(managedCtClass)) {
        writer.insertBefore(
            typeDescriptor.buildInLineDirtyCheckingBodyFragment(
                enhancementContext, persistentField));
      }

      handleCompositeField(managedCtClass, persistentField, writer);

      if (enhancementContext.doBiDirectionalAssociationManagement(persistentField)) {
        handleBiDirectionalAssociation(managedCtClass, persistentField, writer);
      }
      return writer;
    } catch (CannotCompileException cce) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field writer method [%s]",
              managedCtClass.getName(), writerName);
      throw new EnhancementException(msg, cce);
    } catch (NotFoundException nfe) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field writer method [%s]",
              managedCtClass.getName(), writerName);
      throw new EnhancementException(msg, nfe);
    }
  }
 /**
  * Build the method documentation.
  *
  * @param node the XML element that specifies which components to document
  * @param memberDetailsTree the content tree to which the documentation will be added
  */
 public void buildMethodDoc(XMLNode node, Content memberDetailsTree) {
   if (writer == null) {
     return;
   }
   int size = methods.size();
   if (size > 0) {
     Content methodDetailsTree = writer.getMethodDetailsTreeHeader(classDoc, memberDetailsTree);
     for (currentMethodIndex = 0; currentMethodIndex < size; currentMethodIndex++) {
       Content methodDocTree =
           writer.getMethodDocTreeHeader(
               (MethodDoc) methods.get(currentMethodIndex), methodDetailsTree);
       buildChildren(node, methodDocTree);
       methodDetailsTree.addContent(
           writer.getMethodDoc(methodDocTree, (currentMethodIndex == size - 1)));
     }
     memberDetailsTree.addContent(writer.getMethodDetails(methodDetailsTree));
   }
 }
Exemple #5
0
 /**
  * Pops a type from the output frame stack.
  *
  * @param desc the descriptor of the type to be popped. Can also be a method descriptor (in this
  *     case this method pops the types corresponding to the method arguments).
  */
 private void pop(final String desc) {
   char c = desc.charAt(0);
   if (c == '(') {
     pop((MethodWriter.getArgumentsAndReturnSizes(desc) >> 2) - 1);
   } else if (c == 'J' || c == 'D') {
     pop(2);
   } else {
     pop(1);
   }
 }
  /**
   * Build the comments for the method. Do nothing if {@link Configuration#nocomment} is set to
   * true.
   *
   * @param node the XML element that specifies which components to document
   * @param methodDocTree the content tree to which the documentation will be added
   */
  public void buildMethodComments(XMLNode node, Content methodDocTree) {
    if (!configuration.nocomment) {
      MethodDoc method = (MethodDoc) methods.get(currentMethodIndex);

      if (method.inlineTags().length == 0) {
        DocFinder.Output docs = DocFinder.search(configuration, new DocFinder.Input(method));
        method =
            docs.inlineTags != null && docs.inlineTags.length > 0
                ? (MethodDoc) docs.holder
                : method;
      }
      // NOTE:  When we fix the bug where ClassDoc.interfaceTypes() does
      //       not pass all implemented interfaces, holder will be the
      //       interface type.  For now, it is really the erasure.
      writer.addComments(method.containingClass(), method, methodDocTree);
    }
  }
  private void handleCompositeField(
      CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
      throws NotFoundException, CannotCompileException {
    if (!persistentField.hasAnnotation(Embedded.class)) {
      return;
    }

    // make sure to add the CompositeOwner interface
    managedCtClass.addInterface(classPool.get(CompositeOwner.class.getName()));

    if (enhancementContext.isCompositeClass(managedCtClass)) {
      // if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method
      // as well
      MethodWriter.write(
          managedCtClass,
          ""
              + "public void %1$s(String name) {%n"
              + "  if (%2$s != null) { %2$s.callOwner(\".\" + name) ; }%n}",
          EnhancerConstants.TRACKER_CHANGER_NAME,
          EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME);
    }

    // cleanup previous owner
    fieldWriter.insertBefore(
        String.format(
            "" + "if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
            persistentField.getName(),
            CompositeTracker.class.getName(),
            EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER));

    // trigger track changes
    fieldWriter.insertAfter(
        String.format(
            "" + "((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this);%n" + "%5$s(\"%1$s\");",
            persistentField.getName(),
            CompositeTracker.class.getName(),
            CompositeOwner.class.getName(),
            EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
            EnhancerConstants.TRACKER_CHANGER_NAME));
  }
 /**
  * Build the tag information.
  *
  * @param node the XML element that specifies which components to document
  * @param methodDocTree the content tree to which the documentation will be added
  */
 public void buildTagInfo(XMLNode node, Content methodDocTree) {
   writer.addTags((MethodDoc) methods.get(currentMethodIndex), methodDocTree);
 }
 /**
  * Build the deprecation information.
  *
  * @param node the XML element that specifies which components to document
  * @param methodDocTree the content tree to which the documentation will be added
  */
 public void buildDeprecationInfo(XMLNode node, Content methodDocTree) {
   writer.addDeprecated((MethodDoc) methods.get(currentMethodIndex), methodDocTree);
 }
 /**
  * Build the signature.
  *
  * @param node the XML element that specifies which components to document
  * @param methodDocTree the content tree to which the documentation will be added
  */
 public void buildSignature(XMLNode node, Content methodDocTree) {
   methodDocTree.addContent(writer.getSignature((MethodDoc) methods.get(currentMethodIndex)));
 }
Exemple #11
0
 /**
  * Returns the bytecode of the class that was build with this class writer.
  *
  * @return the bytecode of the class that was build with this class writer.
  */
 public byte[] toByteArray() {
   // computes the real size of the bytecode of this class
   int size = 24 + 2 * interfaceCount;
   int nbFields = 0;
   FieldWriter fb = firstField;
   while (fb != null) {
     ++nbFields;
     size += fb.getSize();
     fb = fb.next;
   }
   int nbMethods = 0;
   MethodWriter mb = firstMethod;
   while (mb != null) {
     ++nbMethods;
     size += mb.getSize();
     mb = mb.next;
   }
   int attributeCount = 0;
   if (signature != 0) {
     ++attributeCount;
     size += 8;
     newUTF8("Signature");
   }
   if (sourceFile != 0) {
     ++attributeCount;
     size += 8;
     newUTF8("SourceFile");
   }
   if (sourceDebug != null) {
     ++attributeCount;
     size += sourceDebug.length + 4;
     newUTF8("SourceDebugExtension");
   }
   if (enclosingMethodOwner != 0) {
     ++attributeCount;
     size += 10;
     newUTF8("EnclosingMethod");
   }
   if ((access & Opcodes.ACC_DEPRECATED) != 0) {
     ++attributeCount;
     size += 6;
     newUTF8("Deprecated");
   }
   if ((access & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xffff) < Opcodes.V1_5) {
     ++attributeCount;
     size += 6;
     newUTF8("Synthetic");
   }
   if (innerClasses != null) {
     ++attributeCount;
     size += 8 + innerClasses.length;
     newUTF8("InnerClasses");
   }
   if (anns != null) {
     ++attributeCount;
     size += 8 + anns.getSize();
     newUTF8("RuntimeVisibleAnnotations");
   }
   if (ianns != null) {
     ++attributeCount;
     size += 8 + ianns.getSize();
     newUTF8("RuntimeInvisibleAnnotations");
   }
   if (attrs != null) {
     attributeCount += attrs.getCount();
     size += attrs.getSize(this, null, 0, -1, -1);
   }
   size += pool.length;
   // allocates a byte vector of this size, in order to avoid unnecessary
   // arraycopy operations in the ByteVector.enlarge() method
   ByteVector out = new ByteVector(size);
   out.putInt(0xCAFEBABE).putInt(version);
   out.putShort(index).putByteArray(pool.data, 0, pool.length);
   out.putShort(access).putShort(name).putShort(superName);
   out.putShort(interfaceCount);
   for (int i = 0; i < interfaceCount; ++i) {
     out.putShort(interfaces[i]);
   }
   out.putShort(nbFields);
   fb = firstField;
   while (fb != null) {
     fb.put(out);
     fb = fb.next;
   }
   out.putShort(nbMethods);
   mb = firstMethod;
   while (mb != null) {
     mb.put(out);
     mb = mb.next;
   }
   out.putShort(attributeCount);
   if (signature != 0) {
     out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
   }
   if (sourceFile != 0) {
     out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile);
   }
   if (sourceDebug != null) {
     int len = sourceDebug.length - 2;
     out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
     out.putByteArray(sourceDebug.data, 2, len);
   }
   if (enclosingMethodOwner != 0) {
     out.putShort(newUTF8("EnclosingMethod")).putInt(4);
     out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
   }
   if ((access & Opcodes.ACC_DEPRECATED) != 0) {
     out.putShort(newUTF8("Deprecated")).putInt(0);
   }
   if ((access & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xffff) < Opcodes.V1_5) {
     out.putShort(newUTF8("Synthetic")).putInt(0);
   }
   if (innerClasses != null) {
     out.putShort(newUTF8("InnerClasses"));
     out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
     out.putByteArray(innerClasses.data, 0, innerClasses.length);
   }
   if (anns != null) {
     out.putShort(newUTF8("RuntimeVisibleAnnotations"));
     anns.put(out);
   }
   if (ianns != null) {
     out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
     ianns.put(out);
   }
   if (attrs != null) {
     attrs.put(this, null, 0, -1, -1, out);
   }
   if (invalidFrames) {
     ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
     new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES);
     return cw.toByteArray();
   }
   return out.data;
 }
  private void handleBiDirectionalAssociation(
      CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
      throws NotFoundException, CannotCompileException {
    if (!isPossibleBiDirectionalAssociation(persistentField)) {
      return;
    }
    final CtClass targetEntity = getTargetEntityClass(persistentField);
    if (targetEntity == null) {
      log.debugf(
          "Could not find type of bi-directional association for field [%s#%s]",
          managedCtClass.getName(), persistentField.getName());
      return;
    }
    final String mappedBy = getMappedBy(persistentField, targetEntity);
    if (mappedBy.isEmpty()) {
      log.warnf(
          "Could not find bi-directional association for field [%s#%s]",
          managedCtClass.getName(), persistentField.getName());
      return;
    }

    // create a temporary getter and setter on the target entity to be able to compile our code
    final String mappedByGetterName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy;
    final String mappedBySetterName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy;
    MethodWriter.addGetter(targetEntity, mappedBy, mappedByGetterName);
    MethodWriter.addSetter(targetEntity, mappedBy, mappedBySetterName);

    if (persistentField.hasAnnotation(OneToOne.class)) {
      // only unset when $1 != null to avoid recursion
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null && $1 != null) $0.%<s.%s(null);%n",
              persistentField.getName(), mappedBySetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null && $1.%s() != $0) $1.%s($0);%n",
              mappedByGetterName, mappedBySetterName));
    }
    if (persistentField.hasAnnotation(OneToMany.class)) {
      // only remove elements not in the new collection or else we would loose those elements
      // don't use iterator to avoid ConcurrentModException
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s(null); } }%n",
              persistentField.getName(), targetEntity.getName(), mappedBySetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if (target.%s() != $0) target.%s((%s)$0); } }%n",
              targetEntity.getName(),
              mappedByGetterName,
              mappedBySetterName,
              managedCtClass.getName()));
    }
    if (persistentField.hasAnnotation(ManyToOne.class)) {
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%1$s != null && $0.%1$s.%2$s() != null) $0.%1$s.%2$s().remove($0);%n",
              persistentField.getName(), mappedByGetterName));
      // check .contains($0) to avoid double inserts (but preventing duplicates)
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { java.util.Collection c = $1.%s(); if (c != null && !c.contains($0)) c.add($0); }%n",
              mappedByGetterName));
    }
    if (persistentField.hasAnnotation(ManyToMany.class)) {
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s().remove($0); } }%n",
              persistentField.getName(), targetEntity.getName(), mappedByGetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; java.util.Collection c = target.%s(); if ( c != $0 && c != null) c.add($0); } }%n",
              targetEntity.getName(), mappedByGetterName));
    }
    // implementation note: association management @OneToMany and @ManyToMay works for add()
    // operations but for remove() a snapshot of the collection is needed so we know what
    // associations to break.
    // another approach that could force that behavior would be to return
    // Collections.unmodifiableCollection() ...
  }