/* * Copyright 2016 NAVER Corp. * * Licensed 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.navercorp.pinpoint.profiler.instrument; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException; import com.navercorp.pinpoint.bootstrap.instrument.aspect.Aspect; import com.navercorp.pinpoint.bootstrap.instrument.aspect.JointPoint; import com.navercorp.pinpoint.bootstrap.instrument.aspect.PointCut; import org.objectweb.asm.Opcodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; /** * @author jaehong.kim */ public class ASMAspectWeaver { private static final MethodNameReplacer DEFAULT_METHOD_NAME_REPLACER = new DefaultMethodNameReplacer(); private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final MethodNameReplacer methodNameReplacer; public ASMAspectWeaver() { this(DEFAULT_METHOD_NAME_REPLACER); } public ASMAspectWeaver(final MethodNameReplacer methodNameReplacer) { this.methodNameReplacer = methodNameReplacer; } public void weaving(final ASMClassNodeAdapter sourceClassNode, final ASMClassNodeAdapter adviceClassNode) throws InstrumentException { if (sourceClassNode == null || adviceClassNode == null) { throw new InstrumentException("source and advice class node must not be null."); } if (logger.isInfoEnabled()) { logger.info("weaving sourceClass={} adviceClass={}", sourceClassNode.getInternalName(), adviceClassNode.getInternalName()); } if (!adviceClassNode.hasAnnotation(Aspect.class)) { throw new InstrumentException("@Aspect not found. adviceClass=" + adviceClassNode.getInternalName()); } // advice class hierarchy check. final boolean isSubclass = adviceClassNode.subclassOf(sourceClassNode.getInternalName()); if (!isSubclass) { final String superClassInternalName = adviceClassNode.getSuperClassInternalName(); if (superClassInternalName == null || !superClassInternalName.equals("java/lang/Object")) { throw new InstrumentException("invalid class hierarchy. source class=" + sourceClassNode.getInternalName() + ", advice class=" + adviceClassNode.getInternalName() + ", super class=" + superClassInternalName); } } // find annotation methods and copy utility methods. final MethodNodes methodNodes = findMethodNodes(adviceClassNode); copyUtilMethods(methodNodes, sourceClassNode); copyPointCutMethods(methodNodes, sourceClassNode); } private MethodNodes findMethodNodes(final ASMClassNodeAdapter adviceClassNode) { final MethodNodes methodNodes = new MethodNodes(); for (ASMMethodNodeAdapter methodNode : adviceClassNode.getDeclaredMethods()) { if (methodNode.hasAnnotation(PointCut.class)) { methodNodes.pointCuts.add(methodNode); continue; } if (methodNode.hasAnnotation(JointPoint.class)) { methodNodes.jointPoints.add(methodNode); continue; } methodNodes.utils.add(methodNode); } return methodNodes; } private void copyUtilMethods(final MethodNodes methodNodes, final ASMClassNodeAdapter classNode) throws InstrumentException { for (ASMMethodNodeAdapter methodNode : methodNodes.utils) { if (!methodNode.isPrivate()) { throw new InstrumentException("non private UtilMethod unsupported. method=" + methodNode.getLongName()); } classNode.copyMethod(methodNode); } } private void copyPointCutMethods(final MethodNodes methodNodes, final ASMClassNodeAdapter classNode) throws InstrumentException { final ASMMethodInsnNodeRemapper remapper = new ASMMethodInsnNodeRemapper(); for (ASMMethodNodeAdapter joinPointMethodNode : methodNodes.jointPoints) { remapper.addFilter(null, joinPointMethodNode.getName(), joinPointMethodNode.getDesc()); } for (ASMMethodNodeAdapter pointCutMethodNode : methodNodes.pointCuts) { final ASMMethodNodeAdapter sourceMethodNode = classNode.getDeclaredMethod(pointCutMethodNode.getName(), pointCutMethodNode.getDesc()); if (sourceMethodNode == null) { throw new InstrumentException("not found method. " + classNode.getInternalName() + "." + pointCutMethodNode.getName()); } if (!sourceMethodNode.getDesc().equals(pointCutMethodNode.getDesc())) { throw new InstrumentException("Signature miss match. method=" + pointCutMethodNode.getName() + ", source=" + sourceMethodNode.getDesc() + ", advice=" + pointCutMethodNode.getDesc()); } if (logger.isInfoEnabled()) { logger.info("weaving method={}{}", sourceMethodNode.getName(), sourceMethodNode.getDesc()); } // rename. final String methodName = this.methodNameReplacer.replaceMethodName(sourceMethodNode.getName()); sourceMethodNode.rename(methodName); // set private. sourceMethodNode.setAccess(sourceMethodNode.getAccess() & -6 | Opcodes.ACC_PRIVATE); // copy. classNode.copyMethod(pointCutMethodNode); // remap final ASMMethodNodeAdapter newMethodNode = classNode.getDeclaredMethod(pointCutMethodNode.getName(), pointCutMethodNode.getDesc()); if (newMethodNode == null) { throw new InstrumentException("not found new method. " + classNode.getInternalName() + "." + pointCutMethodNode.getName()); } remapper.setName(methodName); newMethodNode.remapMethodInsnNode(remapper); } } private static class MethodNodes { public final List<ASMMethodNodeAdapter> pointCuts = new ArrayList<ASMMethodNodeAdapter>(); public final List<ASMMethodNodeAdapter> jointPoints = new ArrayList<ASMMethodNodeAdapter>(); public final List<ASMMethodNodeAdapter> utils = new ArrayList<ASMMethodNodeAdapter>(); } }