Java tutorial
/* * Copyright 2012-2015 the original author or authors. * * 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 org.glowroot.weaving; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; import org.objectweb.asm.commons.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.glowroot.api.weaving.BindParameter; import org.glowroot.api.weaving.BindTraveler; import org.glowroot.api.weaving.IsEnabled; import org.glowroot.api.weaving.OnAfter; import org.glowroot.api.weaving.OnBefore; import org.glowroot.api.weaving.OnReturn; import org.glowroot.api.weaving.OnThrow; import org.glowroot.weaving.AdviceFlowOuterHolder.AdviceFlowHolder; import static com.google.common.base.Preconditions.checkNotNull; class WeavingMethodVisitor extends AdviceAdapter { private static final Logger logger = LoggerFactory.getLogger(WeavingMethodVisitor.class); private static final Type adviceFlowOuterHolderType = Type.getType(AdviceFlowOuterHolder.class); private static final Type adviceFlowHolderType = Type.getType(AdviceFlowHolder.class); private static final Type objectType = Type.getObjectType("java/lang/Object"); private final int access; private final String name; private final Type owner; private final ImmutableList<Advice> advisors; private final Type[] argumentTypes; private final Type returnType; private final @Nullable String metaHolderInternalName; private final @Nullable Integer methodMetaGroupUniqueNum; private final boolean bootstrapClassLoader; private final boolean needsOnReturn; private final boolean needsOnThrow; private final @Nullable MethodVisitor outerMethodVisitor; private final Map<Advice, Integer> adviceFlowHolderLocals = Maps.newHashMap(); // the adviceFlow stores the value in the holder at the beginning of the advice so the holder // can be reset at the end of the advice private final Map<Advice, Integer> originalAdviceFlowLocals = Maps.newHashMap(); private final Map<Advice, Integer> enabledLocals = Maps.newHashMap(); private final Map<Advice, Integer> travelerLocals = Maps.newHashMap(); private @MonotonicNonNull Integer returnOpcode; private @MonotonicNonNull Label methodStartLabel; private @MonotonicNonNull Label onReturnLabel; private @MonotonicNonNull Label onCatchStartLabel; private boolean visitedLocalVariableThis; private int[] savedArgLocals = new int[0]; WeavingMethodVisitor(MethodVisitor mv, int access, String name, String desc, Type owner, Iterable<Advice> advisors, @Nullable String metaHolderInternalName, @Nullable Integer methodMetaGroupUniqueNum, boolean bootstrapClassLoader, @Nullable MethodVisitor outerMethodVisitor) { super(ASM5, mv, access, name, desc); this.access = access; this.name = name; this.owner = owner; this.advisors = ImmutableList.copyOf(advisors); argumentTypes = Type.getArgumentTypes(desc); returnType = Type.getReturnType(desc); this.metaHolderInternalName = metaHolderInternalName; this.methodMetaGroupUniqueNum = methodMetaGroupUniqueNum; this.bootstrapClassLoader = bootstrapClassLoader; boolean needsOnReturn = false; boolean needsOnThrow = false; for (Advice advice : advisors) { if (advice.pointcut().ignoreSelfNested() || advice.onAfterAdvice() != null) { needsOnReturn = true; needsOnThrow = true; break; } if (advice.onReturnAdvice() != null) { needsOnReturn = true; } if (advice.onThrowAdvice() != null) { needsOnThrow = true; } } this.needsOnReturn = needsOnReturn; this.needsOnThrow = needsOnThrow; this.outerMethodVisitor = outerMethodVisitor; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (outerMethodVisitor != null) { return outerMethodVisitor.visitAnnotation(desc, visible); } return super.visitAnnotation(desc, visible); } @Override protected void onMethodEnter() { methodStartLabel = new Label(); visitLabel(methodStartLabel); // enabled and traveler locals must be defined outside of the try block so they will be // accessible in the catch block for (Advice advice : advisors) { defineAndEvaluateEnabledLocalVar(advice); defineTravelerLocalVar(advice); } // all advice should be executed outside of the try/catch, otherwise a programming error in // the advice will trigger @OnThrow which is confusing at best for (Advice advice : advisors) { invokeOnBefore(advice, travelerLocals.get(advice)); } saveArgsForMethodExit(); if (needsOnReturn) { onReturnLabel = new Label(); } if (needsOnThrow) { onCatchStartLabel = new Label(); visitLabel(onCatchStartLabel); } } @Override public void visitInsn(int opcode) { if (needsOnReturn && isReturnOpcode(opcode)) { // ATHROW not included, instructions to catch throws will be written (if necessary) in // visitMaxs checkNotNull(onReturnLabel, "Call to onMethodEnter() is required"); returnOpcode = opcode; visitJumpInsn(GOTO, onReturnLabel); } else { super.visitInsn(opcode); } } private static boolean isReturnOpcode(int opcode) { return opcode >= IRETURN && opcode <= RETURN; } @Override public void visitLocalVariable(String name, String desc, @Nullable String signature, Label start, Label end, int index) { // the JSRInlinerAdapter writes the local variable "this" across different label ranges // so visitedLocalVariableThis is checked and updated to ensure this block is only executed // once per method // if (!name.equals("this") || visitedLocalVariableThis) { super.visitLocalVariable(name, desc, signature, start, end, index); return; } visitedLocalVariableThis = true; checkNotNull(methodStartLabel, "Call to onMethodEnter() is required"); // this is only so that eclipse debugger will not display <unknown receiving type> // inside code when inside of code before the previous method start label // (the debugger asks for "this", which is not otherwise available in the new code // inserted at the beginning of the method) // // ClassReader always visits local variables at the very end (just prior to visitMaxs) // so this is a good place to put the outer end label for the local variable 'this' Label outerEndLabel = new Label(); visitLabel(outerEndLabel); super.visitLocalVariable(name, desc, signature, methodStartLabel, outerEndLabel, index); // at the same time, may as well define local vars for enabled and traveler as // applicable for (int i = 0; i < advisors.size(); i++) { Advice advice = advisors.get(i); Integer adviceFlowHolderLocalIndex = adviceFlowHolderLocals.get(advice); if (adviceFlowHolderLocalIndex != null) { super.visitLocalVariable("glowroot$advice$flow$holder$" + i, adviceFlowHolderType.getDescriptor(), null, methodStartLabel, outerEndLabel, adviceFlowHolderLocalIndex); } Integer adviceFlowLocalIndex = originalAdviceFlowLocals.get(advice); if (adviceFlowLocalIndex != null) { super.visitLocalVariable("glowroot$advice$flow$" + i, Type.BOOLEAN_TYPE.getDescriptor(), null, methodStartLabel, outerEndLabel, adviceFlowLocalIndex); } Integer enabledLocalIndex = enabledLocals.get(advice); if (enabledLocalIndex != null) { super.visitLocalVariable("glowroot$enabled$" + i, Type.BOOLEAN_TYPE.getDescriptor(), null, methodStartLabel, outerEndLabel, enabledLocalIndex); } Integer travelerLocalIndex = travelerLocals.get(advice); if (travelerLocalIndex != null) { Type travelerType = advice.travelerType(); if (travelerType == null) { logger.error("visitLocalVariable(): traveler local index is not null," + " but traveler type is null"); } else { super.visitLocalVariable("glowroot$traveler$" + i, travelerType.getDescriptor(), null, methodStartLabel, outerEndLabel, travelerLocalIndex); } } } } @Override public void visitMaxs(int maxStack, int maxLocals) { Label onCatchEndLabel = new Label(); if (needsOnThrow) { visitLabel(onCatchEndLabel); } // returnOpCode can be null if only ATHROW in method in which case method doesn't need // onReturn advice if (needsOnReturn && returnOpcode != null) { checkNotNull(onReturnLabel, "Call to onMethodEnter() is required"); visitLabel(onReturnLabel); for (Advice advice : Lists.reverse(advisors)) { visitOnReturnAdvice(advice, returnOpcode); visitOnAfterAdvice(advice); } resetAdviceFlowIfNecessary(); // need to call super.visitInsn() in order to avoid infinite loop // could call mv.visitInsn(), but that would bypass special constructor handling in // AdviceAdapter.visitInsn() super.visitInsn(returnOpcode); } if (needsOnThrow) { checkNotNull(onCatchStartLabel, "Call to onMethodEnter() is required"); Label onCatchHandlerLabel = new Label(); visitTryCatchBlock(onCatchStartLabel, onCatchEndLabel, onCatchHandlerLabel, "java/lang/Throwable"); visitLabel(onCatchHandlerLabel); visitOnThrowAdvice(); for (Advice advice : Lists.reverse(advisors)) { visitOnAfterAdvice(advice); } resetAdviceFlowIfNecessary(); visitInsn(ATHROW); } super.visitMaxs(maxStack, maxLocals); } private void defineAndEvaluateEnabledLocalVar(Advice advice) { Integer enabledLocal = null; Method isEnabledAdvice = advice.isEnabledAdvice(); if (isEnabledAdvice != null) { loadMethodParameters(advice.isEnabledParameters(), 0, -1, advice.adviceType(), IsEnabled.class, false); visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), isEnabledAdvice.getName(), isEnabledAdvice.getDescriptor(), false); enabledLocal = newLocal(Type.BOOLEAN_TYPE); enabledLocals.put(advice, enabledLocal); storeLocal(enabledLocal); } if (advice.pointcut().ignoreSelfNested()) { // originalAdviceFlowLocal must be defined/initialized outside of any code branches // since it is referenced later on in resetAdviceFlowIfNecessary() int adviceFlowHolderLocal = newLocal(adviceFlowHolderType); adviceFlowHolderLocals.put(advice, adviceFlowHolderLocal); visitInsn(ACONST_NULL); storeLocal(adviceFlowHolderLocal); int originalAdviceFlowLocal = newLocal(Type.BOOLEAN_TYPE); originalAdviceFlowLocals.put(advice, originalAdviceFlowLocal); visitInsn(ICONST_0); storeLocal(originalAdviceFlowLocal); Label setAdviceFlowBlockEnd = new Label(); if (enabledLocal != null) { loadLocal(enabledLocal); visitJumpInsn(IFEQ, setAdviceFlowBlockEnd); } else { enabledLocal = newLocal(Type.BOOLEAN_TYPE); enabledLocals.put(advice, enabledLocal); // temporary initial value to help with Java 7 stack frames visitInsn(ICONST_0); storeLocal(enabledLocal); } visitFieldInsn(GETSTATIC, advice.adviceType().getInternalName(), "glowroot$advice$flow$outer$holder", adviceFlowOuterHolderType.getDescriptor()); visitMethodInsn(INVOKEVIRTUAL, adviceFlowOuterHolderType.getInternalName(), "getInnerHolder", "()" + adviceFlowHolderType.getDescriptor(), false); visitInsn(DUP); storeLocal(adviceFlowHolderLocal); visitMethodInsn(INVOKEVIRTUAL, adviceFlowHolderType.getInternalName(), "isTop", "()Z", false); Label isTopBlockStart = new Label(); visitInsn(DUP); storeLocal(originalAdviceFlowLocal); visitJumpInsn(IFNE, isTopBlockStart); // !isTop() visitInsn(ICONST_0); storeLocal(enabledLocal); visitJumpInsn(GOTO, setAdviceFlowBlockEnd); // enabled visitLabel(isTopBlockStart); loadLocal(adviceFlowHolderLocal); visitInsn(ICONST_0); // note that setTop() is only called if enabled is true, so it only needs to be reset // at the end of the advice if enabled is true visitMethodInsn(INVOKEVIRTUAL, adviceFlowHolderType.getInternalName(), "setTop", "(Z)V", false); visitInsn(ICONST_1); storeLocal(enabledLocal); visitLabel(setAdviceFlowBlockEnd); } } private void defineTravelerLocalVar(Advice advice) { Method onBeforeAdvice = advice.onBeforeAdvice(); if (onBeforeAdvice == null) { return; } Type travelerType = advice.travelerType(); if (travelerType == null) { return; } // have to initialize it with a value, otherwise it won't be defined in the outer scope int travelerLocal = newLocal(travelerType); pushDefault(travelerType); storeLocal(travelerLocal); travelerLocals.put(advice, travelerLocal); } private void invokeOnBefore(Advice advice, @Nullable Integer travelerLocal) { Method onBeforeAdvice = advice.onBeforeAdvice(); if (onBeforeAdvice == null) { return; } Integer enabledLocal = enabledLocals.get(advice); Label onBeforeBlockEnd = null; if (enabledLocal != null) { onBeforeBlockEnd = new Label(); loadLocal(enabledLocal); visitJumpInsn(IFEQ, onBeforeBlockEnd); } loadMethodParameters(advice.onBeforeParameters(), 0, -1, advice.adviceType(), OnBefore.class, false); visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), onBeforeAdvice.getName(), onBeforeAdvice.getDescriptor(), false); if (travelerLocal != null) { storeLocal(travelerLocal); } if (onBeforeBlockEnd != null) { visitLabel(onBeforeBlockEnd); } } private void saveArgsForMethodExit() { int numSavedArgs = getNumSavedArgsNeeded(); if (numSavedArgs == 0) { return; } savedArgLocals = new int[numSavedArgs]; for (int i = 0; i < numSavedArgs; i++) { savedArgLocals[i] = newLocal(argumentTypes[i]); loadArg(i); storeLocal(savedArgLocals[i]); } } private int getNumSavedArgsNeeded() { int numSaveArgsNeeded = 0; for (Advice advice : advisors) { numSaveArgsNeeded = Math.max(numSaveArgsNeeded, getNum(advice.onReturnParameters())); numSaveArgsNeeded = Math.max(numSaveArgsNeeded, getNum(advice.onAfterParameters())); numSaveArgsNeeded = Math.max(numSaveArgsNeeded, getNum(advice.onThrowParameters())); } return numSaveArgsNeeded; } private int getNum(ImmutableList<AdviceParameter> adviceParameters) { int numSaveArgsNeeded = 0; for (AdviceParameter parameter : adviceParameters) { if (parameter.kind() == ParameterKind.METHOD_ARG_ARRAY) { return argumentTypes.length; } else if (parameter.kind() == ParameterKind.METHOD_ARG) { numSaveArgsNeeded++; } } return numSaveArgsNeeded; } private void visitOnReturnAdvice(Advice advice, int opcode) { Method onReturnAdvice = advice.onReturnAdvice(); if (onReturnAdvice == null) { return; } Integer enabledLocal = enabledLocals.get(advice); Label onReturnBlockEnd = null; if (enabledLocal != null) { onReturnBlockEnd = new Label(); loadLocal(enabledLocal); visitJumpInsn(IFEQ, onReturnBlockEnd); } weaveOnReturnAdvice(opcode, advice, onReturnAdvice); if (onReturnBlockEnd != null) { visitLabel(onReturnBlockEnd); } } private void weaveOnReturnAdvice(int opcode, Advice advice, Method onReturnAdvice) { boolean leaveReturnValueOnStack = onReturnAdvice.getReturnType().getSort() == Type.VOID; if (onReturnAdvice.getArgumentTypes().length > 0) { // @BindReturn must be the first argument to @OnReturn (if present) int startIndex = 0; AdviceParameter parameter = advice.onReturnParameters().get(0); switch (parameter.kind()) { case RETURN: loadNonOptionalReturnValue(opcode, parameter, leaveReturnValueOnStack); startIndex = 1; break; case OPTIONAL_RETURN: loadOptionalReturnValue(opcode, leaveReturnValueOnStack); startIndex = 1; break; default: // first argument is not @BindReturn break; } loadMethodParameters(advice.onReturnParameters(), startIndex, travelerLocals.get(advice), advice.adviceType(), OnReturn.class, true); } visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), onReturnAdvice.getName(), onReturnAdvice.getDescriptor(), false); } private void loadNonOptionalReturnValue(int opcode, AdviceParameter parameter, boolean dup) { if (opcode == RETURN) { logger.warn("cannot use @BindReturn on a @Pointcut returning void"); pushDefault(parameter.type()); } else { boolean primitive = parameter.type().getSort() < Type.ARRAY; loadReturnValue(opcode, dup, !primitive); } } private void loadOptionalReturnValue(int opcode, boolean dup) { if (opcode == RETURN) { // void visitMethodInsn(INVOKESTATIC, "org/glowroot/weaving/VoidReturn", "getInstance", "()Lorg/glowroot/api/OptionalReturn;", false); } else { loadReturnValue(opcode, dup, true); visitMethodInsn(INVOKESTATIC, "org/glowroot/weaving/NonVoidReturn", "create", "(Ljava/lang/Object;)Lorg/glowroot/api/OptionalReturn;", false); } } private void loadReturnValue(int opcode, boolean dup, boolean autobox) { if (dup) { if (opcode == LRETURN || opcode == DRETURN) { visitInsn(DUP2); } else { visitInsn(DUP); } } if (autobox && opcode != ARETURN && opcode != ATHROW) { box(returnType); } } private void visitOnThrowAdvice() { for (Advice advice : Lists.reverse(advisors)) { Method onThrowAdvice = advice.onThrowAdvice(); if (onThrowAdvice == null) { continue; } Integer enabledLocal = enabledLocals.get(advice); Label onThrowBlockEnd = null; if (enabledLocal != null) { onThrowBlockEnd = new Label(); loadLocal(enabledLocal); visitJumpInsn(IFEQ, onThrowBlockEnd); } if (onThrowAdvice.getArgumentTypes().length == 0) { visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), onThrowAdvice.getName(), onThrowAdvice.getDescriptor(), false); } else { int startIndex = 0; if (advice.onThrowParameters().get(0).kind() == ParameterKind.THROWABLE) { // @BindThrowable must be the first argument to @OnThrow (if present) visitInsn(DUP); startIndex++; } loadMethodParameters(advice.onThrowParameters(), startIndex, travelerLocals.get(advice), advice.adviceType(), OnThrow.class, true); visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), onThrowAdvice.getName(), onThrowAdvice.getDescriptor(), false); } if (onThrowBlockEnd != null) { visitLabel(onThrowBlockEnd); } } } private void visitOnAfterAdvice(Advice advice) { Method onAfterAdvice = advice.onAfterAdvice(); if (onAfterAdvice == null) { return; } Integer enabledLocal = enabledLocals.get(advice); Label onAfterBlockEnd = null; if (enabledLocal != null) { onAfterBlockEnd = new Label(); loadLocal(enabledLocal); visitJumpInsn(IFEQ, onAfterBlockEnd); } loadMethodParameters(advice.onAfterParameters(), 0, travelerLocals.get(advice), advice.adviceType(), OnAfter.class, true); visitMethodInsn(INVOKESTATIC, advice.adviceType().getInternalName(), onAfterAdvice.getName(), onAfterAdvice.getDescriptor(), false); if (onAfterBlockEnd != null) { visitLabel(onAfterBlockEnd); } } private void resetAdviceFlowIfNecessary() { for (Advice advice : advisors) { if (advice.pointcut().ignoreSelfNested()) { Integer enabledLocal = enabledLocals.get(advice); Integer originalAdviceFlowLocal = originalAdviceFlowLocals.get(advice); Integer adviceFlowHolderLocal = adviceFlowHolderLocals.get(advice); // enabledLocal is non-null for all advice checkNotNull(enabledLocal, "enabledLocal is null"); // adviceFlowLocal is non-null for all advice with ignoreSelfNested = true // (same condition as tested above) checkNotNull(originalAdviceFlowLocal, "originalAdviceFlowLocal is null"); // adviceFlowHolderLocal is non-null for all advice with ignoreSelfNested = true // (same condition as tested above) checkNotNull(adviceFlowHolderLocal, "adviceFlowHolderLocal is null"); Label setAdviceFlowBlockEnd = new Label(); loadLocal(enabledLocal); visitJumpInsn(IFEQ, setAdviceFlowBlockEnd); loadLocal(originalAdviceFlowLocal); visitJumpInsn(IFEQ, setAdviceFlowBlockEnd); // isTop was true at the beginning of the advice, need to reset it now loadLocal(adviceFlowHolderLocal); visitInsn(ICONST_1); visitMethodInsn(INVOKEVIRTUAL, adviceFlowHolderType.getInternalName(), "setTop", "(Z)V", false); visitLabel(setAdviceFlowBlockEnd); } } } private void loadMethodParameters(List<AdviceParameter> parameters, int startIndex, @Nullable Integer travelerLocal, Type adviceType, Class<? extends Annotation> annotationType, boolean useSavedArgs) { int argIndex = 0; for (int i = startIndex; i < parameters.size(); i++) { AdviceParameter parameter = parameters.get(i); switch (parameter.kind()) { case RECEIVER: loadTarget(); break; case METHOD_ARG: loadMethodParameter(adviceType, annotationType, argIndex++, parameter, useSavedArgs); break; case METHOD_ARG_ARRAY: loadArgArray(useSavedArgs); break; case METHOD_NAME: loadMethodName(); break; case TRAVELER: loadTraveler(travelerLocal, adviceType, annotationType, parameter); break; case CLASS_META: checkNotNull(metaHolderInternalName); loadClassMeta(parameter); break; case METHOD_META: checkNotNull(metaHolderInternalName); checkNotNull(methodMetaGroupUniqueNum); loadMethodMeta(parameter); break; default: // this should have been caught during Advice construction, but just in case: logger.warn("the @{} method in {} has an unexpected parameter kind {} at index" + " {}", annotationType.getSimpleName(), adviceType.getClassName(), parameter.kind(), i); pushDefault(parameter.type()); break; } } } private void loadTarget() { if (!Modifier.isStatic(access)) { visitVarInsn(ALOAD, 0); } else { // cannot use push(Type) since .class constants are not supported in classes // that were compiled to jdk 1.4 visitLdcInsn(owner.getClassName()); visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); } } private void loadMethodParameter(Type adviceType, Class<? extends Annotation> annotationType, int argIndex, AdviceParameter parameter, boolean useSavedArg) { if (argIndex >= argumentTypes.length) { logger.warn( "the @{} method in {} has more @{} arguments than the number of args in" + " the target method", annotationType.getSimpleName(), adviceType.getClassName(), BindParameter.class.getSimpleName()); pushDefault(parameter.type()); return; } if (useSavedArg) { loadLocal(savedArgLocals[argIndex]); } else { loadArg(argIndex); } boolean primitive = parameter.type().getSort() < Type.ARRAY; if (!primitive) { // autobox box(argumentTypes[argIndex]); } } private void loadArgArray(boolean useSavedArgs) { push(argumentTypes.length); newArray(objectType); for (int i = 0; i < argumentTypes.length; i++) { dup(); push(i); if (useSavedArgs) { loadLocal(savedArgLocals[i]); } else { loadArg(i); } box(argumentTypes[i]); arrayStore(objectType); } } private void loadMethodName() { if (name.contains("$glowroot$timer$")) { // strip off internal timer identifier from method name visitLdcInsn(name.substring(0, name.indexOf("$glowroot$timer$"))); } else { visitLdcInsn(name); } } private void loadTraveler(@Nullable Integer travelerLocal, Type adviceType, Class<? extends Annotation> annotationType, AdviceParameter parameter) { if (travelerLocal == null) { logger.warn("the @{} method in {} requested @{} but @{} returns void", annotationType.getSimpleName(), adviceType.getClassName(), BindTraveler.class.getSimpleName(), OnBefore.class.getSimpleName()); pushDefault(parameter.type()); } else { loadLocal(travelerLocal); } } @RequiresNonNull("metaHolderInternalName") private void loadClassMeta(AdviceParameter parameter) { Type classMetaFieldType = parameter.type(); String classMetaFieldName = "glowroot$class$meta$" + classMetaFieldType.getInternalName().replace('/', '$'); if (bootstrapClassLoader) { int index = BootstrapMetaHolders.reserveClassMetaHolderIndex(metaHolderInternalName, classMetaFieldName); push(index); visitMethodInsn(INVOKESTATIC, "org/glowroot/weaving/BootstrapMetaHolders", "getClassMeta", "(I)Ljava/lang/Object;", false); } else { visitFieldInsn(GETSTATIC, metaHolderInternalName, classMetaFieldName, classMetaFieldType.getDescriptor()); } } @RequiresNonNull({ "metaHolderInternalName", "methodMetaGroupUniqueNum" }) private void loadMethodMeta(AdviceParameter parameter) { Type methodMetaFieldType = parameter.type(); String methodMetaFieldName = "glowroot$method$meta$" + methodMetaGroupUniqueNum + '$' + methodMetaFieldType.getInternalName().replace('/', '$'); if (bootstrapClassLoader) { int index = BootstrapMetaHolders.reserveMethodMetaHolderIndex(metaHolderInternalName, methodMetaFieldName); push(index); visitMethodInsn(INVOKESTATIC, "org/glowroot/weaving/BootstrapMetaHolders", "getMethodMeta", "(I)Ljava/lang/Object;", false); } else { visitFieldInsn(GETSTATIC, metaHolderInternalName, methodMetaFieldName, methodMetaFieldType.getDescriptor()); } } private void pushDefault(Type type) { switch (type.getSort()) { case Type.BOOLEAN: push(false); break; case Type.CHAR: case Type.BYTE: case Type.SHORT: case Type.INT: visitInsn(ICONST_0); break; case Type.FLOAT: visitInsn(FCONST_0); break; case Type.LONG: visitInsn(LCONST_0); break; case Type.DOUBLE: visitInsn(DCONST_0); break; default: visitInsn(ACONST_NULL); break; } } }