All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.jpa.weaving.MethodWeaver Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     19/04/2014-2.6 Lukas Jungmann
//       - 429992: JavaSE 8/ASM 5.0.1 support (EclipseLink silently ignores Entity classes with lambda expressions)
 package org.eclipse.persistence.internal.jpa.weaving;

//ASM imports
import org.eclipse.persistence.asm.ASMFactory;
import org.eclipse.persistence.asm.AnnotationVisitor;
import org.eclipse.persistence.asm.Attribute;
import org.eclipse.persistence.asm.Label;
import org.eclipse.persistence.asm.EclipseLinkMethodVisitor;
import org.eclipse.persistence.asm.MethodVisitor;
import org.eclipse.persistence.asm.Opcodes;
import org.eclipse.persistence.asm.Type;

import org.eclipse.persistence.internal.descriptors.VirtualAttributeMethodInfo;

/**
 * Processes all the methods of a class to weave in persistence code such as,
 * lazy value holder, change tracking and fetch groups.
 *
 * For FIELD access, changes references to GETFIELD and PUTFIELD to call weaved get/set methods.
 *
 * For Property access, modifies the getters and setters.
 *
 */

public class MethodWeaver extends EclipseLinkMethodVisitor {

    protected ClassWeaver tcw;
    protected String methodName;
    protected String methodDescriptor = null;

    /** Determines if we are at the first line of a method. */
    protected boolean methodStarted = false;

    public MethodWeaver(ClassWeaver tcw, String methodName, String methodDescriptor, MethodVisitor mv) {
        super(mv);
        this.setCustomMethodVisitor(this);
        this.tcw = tcw;
        this.methodName = methodName;
        this.methodDescriptor = methodDescriptor;
    }

    @Override
    public void visitInsn (final int opcode) {
        weaveBeginningOfMethodIfRequired();
        if (opcode == Opcodes.RETURN) {
            weaveEndOfMethodIfRequired();
        }
        super.visitInsnSuper(opcode);
    }

    @Override
    public void visitIntInsn (final int opcode, final int operand) {
        weaveBeginningOfMethodIfRequired();
        super.visitIntInsnSuper(opcode, operand);
    }

    @Override
    public void visitVarInsn (final int opcode, final int var) {
        weaveBeginningOfMethodIfRequired();
        super.visitVarInsnSuper(opcode, var);
    }

    @Override
    public void visitTypeInsn (final int opcode, final String desc) {
        weaveBeginningOfMethodIfRequired();
        super.visitTypeInsnSuper(opcode, desc);
    }

    @Override
    public void visitFieldInsn (final int opcode, final String owner, final String name, final String desc) {
        weaveBeginningOfMethodIfRequired();
        weaveAttributesIfRequired(opcode, owner, name, desc);
    }

    @Override
    public void visitMethodInsn (final int opcode, final String owner, final String name, final String desc, boolean intf) {
        weaveBeginningOfMethodIfRequired();
        String descClassName = "";
        if (desc.length() > 3){
            descClassName = desc.substring(3, desc.length()-1);
        }
        // Need to find super.clone and add _persistence_post_clone(clone).
        if (this.tcw.classDetails.shouldWeaveInternal() && name.equals("clone") &&
                /* the following will return true if we are calling a method stored on our direct superclass or one of its superclasses
                 * that is involved in our metadata hierarchy and if there are no classes farther up the hierarchy that implement a clone method
                 * The goal is to call _persistence_post_clone() at the highest level in the hierarchy possible
                 * For completeness, we check to ensure the return type is in that same hierarchy */
                this.tcw.classDetails.isInSuperclassHierarchy(owner) && this.tcw.classDetails.isInMetadataHierarchy(descClassName) &&
                (this.tcw.classDetails.getNameOfSuperclassImplementingCloneMethod() == null)) {
            super.visitMethodInsnSuper(opcode, owner, name, desc, intf);
            super.visitTypeInsnSuper(Opcodes.CHECKCAST, this.tcw.classDetails.getClassName());
            super.visitMethodInsnSuper(Opcodes.INVOKEVIRTUAL, this.tcw.classDetails.getClassName(), "_persistence_post_clone", "()Ljava/lang/Object;", false);
        } else {
            super.visitMethodInsnSuper(opcode, owner, name, desc, intf);
        }
    }

    @Override
    public void visitJumpInsn (final int opcode, final Label label) {
        weaveBeginningOfMethodIfRequired();
        super.visitJumpInsnSuper(opcode, label);
    }

    @Override
    public void visitLabel (final Label label) {
        weaveBeginningOfMethodIfRequired();
        super.visitLabelSuper(label);
    }

    @Override
    public void visitLdcInsn (final Object cst) {
        weaveBeginningOfMethodIfRequired();
        super.visitLdcInsnSuper(cst);
    }

    @Override
    public void visitIincInsn (final int var, final int increment) {
        weaveBeginningOfMethodIfRequired();
        super.visitIincInsnSuper(var, increment);
    }

    @Override
    public void visitTableSwitchInsn (final int min, final int max, final Label dflt, final Label... labels) {
        weaveBeginningOfMethodIfRequired();
        super.visitTableSwitchInsnSuper(min, max, dflt, labels);
    }

    @Override
    public void visitLookupSwitchInsn (final Label dflt, final int keys[], final Label labels[]) {
        weaveBeginningOfMethodIfRequired();
        super.visitLookupSwitchInsnSuper(dflt, keys, labels);
    }

    @Override
    public void visitMultiANewArrayInsn (final String desc, final int dims) {
        weaveBeginningOfMethodIfRequired();
        super.visitMultiANewArrayInsnSuper(desc, dims);
    }

    @Override
    public void visitTryCatchBlock (final Label start, final Label end,final Label handler, final String type) {
        weaveBeginningOfMethodIfRequired();
        super.visitTryCatchBlockSuper(start, end, handler, type);
    }

    @Override
    public void visitMaxs (final int maxStack, final int maxLocals) {
        weaveBeginningOfMethodIfRequired();
        super.visitMaxsSuper(maxStack, maxLocals);
    }

    @Override
    public void visitLocalVariable (final String name, final String desc, String signature, final Label start, final Label end, final int index) {
        weaveBeginningOfMethodIfRequired();
        super.visitLocalVariableSuper(name, desc, signature, start, end, index);
    }

    @Override
    public void visitLineNumber (final int line, final Label start) {
        weaveBeginningOfMethodIfRequired();
        super.visitLineNumberSuper(line, start);
    }

    @Override
    public void visitAttribute (final Attribute attr) {
        weaveBeginningOfMethodIfRequired();
        super.visitAttributeSuper(attr);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        return super.visitAnnotationSuper(desc, visible);
    }

    @Override
    public void visitEnd() {
        // Nothing to do.
    }

    /**
     * Change GETFIELD and PUTFIELD for fields that use attribute access to make use of new convenience methods.
     *
     * A GETFIELD for an attribute named 'variableName' will be replaced by a call to:
     *
     * _persistence_get_variableName()
     *
     * A PUTFIELD for an attribute named 'variableName' will be replaced by a call to:
     *
     * _persistence_set_variableName(variableName)
     */
    public void weaveAttributesIfRequired(int opcode, String owner, String name, String desc) {
        AttributeDetails attributeDetails = tcw.classDetails.getAttributeDetailsFromClassOrSuperClass(name);
        if ((attributeDetails == null) || (!attributeDetails.hasField()) || (!this.tcw.classDetails.isInMetadataHierarchy(owner))) {
            super.visitFieldInsnSuper(opcode, owner, name, desc);
            return;
        }
        if (opcode == Opcodes.GETFIELD) {
            if (attributeDetails.weaveValueHolders() || tcw.classDetails.shouldWeaveFetchGroups()) {
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_GET + name, "()" + attributeDetails.getReferenceClassType().getDescriptor(), false);
            } else {
                super.visitFieldInsnSuper(opcode, owner, name, desc);
            }
        } else if (opcode == Opcodes.PUTFIELD) {
            if ((attributeDetails.weaveValueHolders()) || (tcw.classDetails.shouldWeaveChangeTracking()) || (tcw.classDetails.shouldWeaveFetchGroups())) {
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_SET + name, "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")V", false);
            } else {
                super.visitFieldInsnSuper(opcode, owner, name, desc);
            }
        }  else {
            super.visitFieldInsnSuper(opcode, owner, name, desc);
        }
    }

    /**
     * Makes modifications to the beginning of a method.
     *
     * 1. Modifies getter method for attributes using property access
     *
     * In a getter method for 'attributeName', the following lines are added at the beginning of the method
     *
     *  _persistence_checkFetched("attributeName");
     *  _persistence_initialize_attributeName_vh();
     *  if (!_persistence_attributeName_vh.isInstantiated()) {
     *      PropertyChangeListener temp_persistence_listener = _persistence_listener;
     *      _persistence_listener = null;
     *      setAttributeName((AttributeType)_persistence_attributeName_vh.getValue());
     *      _persistence_listener = temp_persistence_listener;
     *  }
     *
     *  2. Modifies setter methods to store old value of attribute
     *  If weaving for fetch groups:
     *
     *  // if weaving for change tracking:
     *  if(_persistence_listener != null)
     *      // for Objects
     *      AttributeType oldAttribute = getAttribute()
     *      // for primitives
     *      AttributeWrapperType oldAttribute = new AttributeWrapperType(getAttribute());
     *          e.g. Double oldAttribute = Double.valueOf(getAttribute());
     *  else
     *      _persistence_checkFetchedForSet("attributeName");
     *  _persistence_propertyChange("attributeName", oldAttribute, argument);
     *
     *  otherwise (not weaving for fetch groups):
     *
     *      // for Objects
     *      AttributeType oldAttribute = getAttribute()
     *      // for primitives
     *      AttributeWrapperType oldAttribute = new AttributeWrapperType(getAttribute());
     *          e.g. Double oldAttribute = Double.valueOf(getAttribute());
     *  _persistence_propertyChange("attributeName", oldAttribute, argument);
     *
     *  // if not weaving for change tracking, but for fetch groups only:
     *  _persistence_checkFetchedForSet("attributeName");
     *
     *  3. Modifies getter Method for attributes using virtual access
     *
     *  add: _persistence_checkFetched(name);
     *
     *  4. Modifies setter Method for attributes using virtual access
     *
     *  add code of the following form:
     *
     *   Object obj = null;
     *   if(_persistence_listener != null){
     *      obj = get(name);
     *   } else {
     *       _persistence_checkFetchedForSet(name);
     *   }
     *   _persistence_propertyChange(name, obj, value);
     *
     *   _persistence_checkFetchedForSet(name) call will be excluded if weaving of fetch groups is not enabled
     *
     *   _persistence_propertyChange(name, obj, value); will be excluded if weaving of change tracking is not enabled
     */
    public void weaveBeginningOfMethodIfRequired() {
        if (this.methodStarted){
            return;
        }
        // Must set immediately, as weaving can trigger this method.
        this.methodStarted = true;
        boolean isVirtual = false;
        AttributeDetails attributeDetails = tcw.classDetails.getGetterMethodToAttributeDetails().get(methodName);
        boolean isGetMethod = (attributeDetails != null) && (this.methodDescriptor.startsWith("()") ||
                (attributeDetails.isVirtualProperty() && this.methodDescriptor.startsWith("(" + ClassWeaver.STRING_SIGNATURE +")")));

        String attributeName = null;
        String referenceClassName = null;
        String setterMethodName = null;
        Type referenceClassType = null;
        String getterMethodName = null;
        int valueHoldingLocation = 1;
        int valueStorageLocation = 2;

        if (attributeDetails == null){
            VirtualAttributeMethodInfo info = tcw.classDetails.getInfoForVirtualGetMethod(methodName);
            if ((info != null) && this.methodDescriptor.equals(ClassWeaver.VIRTUAL_GETTER_SIGNATURE) ){
                isGetMethod = true;
                isVirtual = true;
                referenceClassName = "java.lang.Object";
                setterMethodName = info.getSetMethodName();
                referenceClassType = Type.getType(ClassWeaver.OBJECT_SIGNATURE);
                getterMethodName = methodName;
            }
        } else {
            attributeName = attributeDetails.getAttributeName();
            referenceClassName = attributeDetails.getReferenceClassName();
            setterMethodName = attributeDetails.getSetterMethodName();
            referenceClassType = attributeDetails.getReferenceClassType();
            getterMethodName = attributeDetails.getGetterMethodName();
            isVirtual = attributeDetails.isVirtualProperty();
        }
        if (isVirtual){
            valueHoldingLocation = 2;
            valueStorageLocation = 3;
        }
        if (isVirtual || (isGetMethod && !attributeDetails.hasField())) {
            if (tcw.classDetails.shouldWeaveFetchGroups()) {
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                if (isVirtual){
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                } else {
                    methodVisitor.visitLdcInsn(attributeName);
                }
                // _persistence_checkFetched("attributeName");
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_checkFetched", "(Ljava/lang/String;)V", false);
            }
            if (!isVirtual && attributeDetails.weaveValueHolders()) {
                // _persistence_initialize_attributeName_vh();
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_initialize_" + attributeName + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, "()V", false);

                // if (!_persistence_attributeName_vh.isInstantiated()) {
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_FIELDNAME_PREFIX + attributeName + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, ClassWeaver.VHI_SIGNATURE);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "isInstantiated", "()Z", true);
                Label l0 = ASMFactory.createLabel();
                methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);

                // Need to disable change tracking when the set method is called to avoid thinking the attribute changed.
                if (tcw.classDetails.shouldWeaveChangeTracking()) {
                    // PropertyChangeListener temp_persistence_listener = _persistence_listener;
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                    methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), "_persistence_listener", ClassWeaver.PCL_SIGNATURE);
                    methodVisitor.visitVarInsn(Opcodes.ASTORE, 4);
                    // _persistence_listener = null;
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                    methodVisitor.visitInsn(Opcodes.ACONST_NULL);
                    methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, tcw.classDetails.getClassName(), "_persistence_listener", ClassWeaver.PCL_SIGNATURE);
                }
                // setAttributeName((AttributeType)_persistence_attributeName_vh.getValue());
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_FIELDNAME_PREFIX + attributeName + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, ClassWeaver.VHI_SIGNATURE);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "getValue", "()Ljava/lang/Object;", true);
                methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, referenceClassName.replace('.','/'));
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), setterMethodName, "(" + referenceClassType.getDescriptor() + ")V", false);

                if (tcw.classDetails.shouldWeaveChangeTracking()) {
                    // _persistence_listener = temp_persistence_listener;
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 4);
                    methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, tcw.classDetails.getClassName(), "_persistence_listener", ClassWeaver.PCL_SIGNATURE);
                }
                // }
                methodVisitor.visitLabel(l0);
            }
        } else {
            attributeDetails = tcw.classDetails.getSetterMethodToAttributeDetails().get(methodName);
            boolean isSetMethod = (attributeDetails != null) && this.methodDescriptor.equals(attributeDetails.getSetterMethodSignature());
            if (attributeDetails == null){
                VirtualAttributeMethodInfo info = tcw.classDetails.getInfoForVirtualSetMethod(methodName);
                if (info != null && this.methodDescriptor.equals(ClassWeaver.VIRTUAL_GETTER_SIGNATURE) ){
                    isGetMethod = true;
                    isVirtual = true;
                    referenceClassName = "java.lang.Object";
                    setterMethodName = methodName;
                    referenceClassType = Type.getType(ClassWeaver.OBJECT_SIGNATURE);
                    getterMethodName = info.getGetMethodName();
                }
            } else {
                attributeName = attributeDetails.getAttributeName();
                referenceClassName = attributeDetails.getReferenceClassName();
                setterMethodName = attributeDetails.getSetterMethodName();
                referenceClassType = attributeDetails.getReferenceClassType();
                getterMethodName = attributeDetails.getGetterMethodName();
                isVirtual = attributeDetails.isVirtualProperty();
            }
            if (isVirtual){
                valueHoldingLocation = 2;
                valueStorageLocation = 3;
            }
            if (isVirtual || (isSetMethod  && !attributeDetails.hasField())) {
                if(tcw.classDetails.shouldWeaveChangeTracking()) {
                    if(tcw.classDetails.shouldWeaveFetchGroups()) {
                        // if this is a primitive, get the wrapper class
                        String wrapper = ClassWeaver.wrapperFor(referenceClassType.getSort());

                        methodVisitor.visitInsn(Opcodes.ACONST_NULL);
                        if (wrapper != null){
                            methodVisitor.visitVarInsn(Opcodes.ASTORE, valueStorageLocation + 1);
                        } else {
                            methodVisitor.visitVarInsn(Opcodes.ASTORE, valueStorageLocation);
                        }
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                        methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), "_persistence_listener", "Ljava/beans/PropertyChangeListener;");
                        Label l0 = ASMFactory.createLabel();
                        methodVisitor.visitJumpInsn(Opcodes.IFNULL, l0);

                        /**
                         * The code below constructs the following code
                         *
                         * AttributeType oldAttribute = getAttribute() // for Objects
                         *
                         * AttributeWrapperType oldAttribute = new AttributeWrapperType(getAttribute()); // for primitives
                         */
                        // 1st part of invoking constructor for primitives to wrap them
                        if (wrapper != null) {
                            methodVisitor.visitTypeInsn(Opcodes.NEW, wrapper);
                            methodVisitor.visitInsn(Opcodes.DUP);
                        }

                        // Call the getter
                        // getAttribute()
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                        String getterArgument = "";
                        String getterReturn = referenceClassType.getDescriptor();
                        if (isVirtual){
                            getterArgument = ClassWeaver.STRING_SIGNATURE;
                            getterReturn = ClassWeaver.OBJECT_SIGNATURE;
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        }
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), getterMethodName, "(" + getterArgument + ")" + getterReturn, false);
                        if (wrapper != null){
                            // 2nd part of using constructor.
                            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, wrapper, "", "(" + referenceClassType.getDescriptor() + ")V", false);
                            methodVisitor.visitVarInsn(Opcodes.ASTORE,  valueStorageLocation + 1);
                        } else {
                            // store the result
                            methodVisitor.visitVarInsn(Opcodes.ASTORE, valueStorageLocation);
                        }

                        Label l1 = ASMFactory.createLabel();
                        methodVisitor.visitJumpInsn(Opcodes.GOTO, l1);
                        methodVisitor.visitLabel(l0);
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);

                        if (isVirtual){
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        } else {
                            methodVisitor.visitLdcInsn(attributeName);
                        }
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_checkFetchedForSet", "(Ljava/lang/String;)V", false);
                        methodVisitor.visitLabel(l1);

                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);

                        if (isVirtual){
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        } else {
                            methodVisitor.visitLdcInsn(attributeName);
                        }

                        if (wrapper != null) {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, valueStorageLocation + 1);
                            methodVisitor.visitTypeInsn(Opcodes.NEW, wrapper);
                            methodVisitor.visitInsn(Opcodes.DUP);
                        } else {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, valueStorageLocation);
                        }
                        // get an appropriate load opcode for the type
                        int opcode = referenceClassType.getOpcode(Opcodes.ILOAD);
                        methodVisitor.visitVarInsn(opcode, valueHoldingLocation);
                        if (wrapper != null){
                            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, wrapper, "", "(" + referenceClassType.getDescriptor() + ")V", false);
                        }
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_propertyChange", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", false);
                    } else {
                        // tcw.classDetails.shouldWeaveFetchGroups()
                        /**
                         * The code below constructs the following code
                         *
                         * AttributeType oldAttribute = getAttribute() // for Objects
                         *
                         * AttributeWrapperType oldAttribute = new AttributeWrapperType(getAttribute()); // for primitives
                         */
                        // if this is a primitive, get the wrapper class
                        String wrapper = ClassWeaver.wrapperFor(referenceClassType.getSort());

                        // 1st part of invoking constructor for primitives to wrap them
                        if (wrapper != null) {
                            methodVisitor.visitTypeInsn(Opcodes.NEW, wrapper);
                            methodVisitor.visitInsn(Opcodes.DUP); }

                        // Call the getter
                        // getAttribute()
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                        String getterArgument = "";
                        String getterReturn = referenceClassType.getDescriptor();
                        if (isVirtual){
                            getterArgument = ClassWeaver.STRING_SIGNATURE;
                            getterReturn = ClassWeaver.OBJECT_SIGNATURE;
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        }
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), getterMethodName, "(" + getterArgument + ")" + getterReturn, false);
                        if (wrapper != null){
                            // 2nd part of using constructor.
                            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, wrapper, "", "(" + attributeDetails.getReferenceClassType().getDescriptor() + ")V", false);
                            methodVisitor.visitVarInsn(Opcodes.ASTORE, valueStorageLocation + 1);
                        } else {
                            // store the result
                            methodVisitor.visitVarInsn(Opcodes.ASTORE, valueStorageLocation);
                        }

                        // makes use of the value stored in weaveBeginningOfMethodIfRequired to call property change method
                        // _persistence_propertyChange("attributeName", oldAttribute, argument);
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                        if (isVirtual){
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        } else {
                            methodVisitor.visitLdcInsn(attributeName);
                        }
                        if (wrapper != null) {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, valueStorageLocation + 1);
                            methodVisitor.visitTypeInsn(Opcodes.NEW, wrapper);
                            methodVisitor.visitInsn(Opcodes.DUP);
                        } else {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, valueStorageLocation);
                        }
                        int opcode = referenceClassType.getOpcode(Opcodes.ILOAD);
                        methodVisitor.visitVarInsn(opcode, valueHoldingLocation);
                        if (wrapper != null) {
                            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, wrapper, "", "(" + referenceClassType.getDescriptor() + ")V", false);
                        }
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_propertyChange", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", false);
                    }
                } else {
                    // !tcw.classDetails.shouldWeaveChangeTracking()
                    if(tcw.classDetails.shouldWeaveFetchGroups()) {
                        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                        if (isVirtual){
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                        } else {
                            methodVisitor.visitLdcInsn(attributeName);
                        }
                        // _persistence_checkFetchedForSet("variableName");
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_checkFetchedForSet", "(Ljava/lang/String;)V", false);
                    }
                }
            }
        }
    }

    /**
     * Modifies methods just before the return.
     *
     * In a setter method for a LAZY mapping, for 'attributeName', the following lines are added at the end of the method.
     *
     *  _persistence_initialize_attributeName_vh();
     *  _persistence_attributeName_vh.setValue(argument);
     *  _persistence_attributeName_vh.setIsCoordinatedWithProperty(true);
     *
     * In a setter method for a non-LAZY mapping, the followings lines are added if change tracking is activated:
     *
     *  _persistence_propertyChange("attributeName", oldAttribute, argument);
     *
     *  Note: This code will wrap primitives by adding a call to the primitive constructor.
     */
    public void weaveEndOfMethodIfRequired() {
        AttributeDetails attributeDetails = tcw.classDetails.getSetterMethodToAttributeDetails().get(methodName);
        boolean isSetMethod = (attributeDetails != null) && this.methodDescriptor.equals(attributeDetails.getSetterMethodSignature());
        if (isSetMethod  && !attributeDetails.hasField()) {
            if (attributeDetails.weaveValueHolders()) {
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tcw.classDetails.getClassName(), "_persistence_initialize_" + attributeDetails.getAttributeName() + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, "()V", false);

                //_persistence_attributeName_vh.setValue(argument);
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_FIELDNAME_PREFIX + attributeDetails.getAttributeName() + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, ClassWeaver.VHI_SIGNATURE);
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "setValue", "(Ljava/lang/Object;)V", true);

                //  _persistence_attributeName_vh.setIsCoordinatedWithProperty(true);
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, tcw.classDetails.getClassName(), ClassWeaver.PERSISTENCE_FIELDNAME_PREFIX + attributeDetails.getAttributeName() + ClassWeaver.PERSISTENCE_FIELDNAME_POSTFIX, ClassWeaver.VHI_SIGNATURE);
                methodVisitor.visitInsn(Opcodes.ICONST_1);
                methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, ClassWeaver.VHI_SHORT_SIGNATURE, "setIsCoordinatedWithProperty", "(Z)V", true);
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy