1 /******************************************************************************* 2 * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * Marc R. Hoffmann - initial API and implementation 10 * 11 *******************************************************************************/ 12 package org.jacoco.core.internal.instr; 13 14 import static java.lang.String.format; 15 16 import org.objectweb.asm.MethodVisitor; 17 import org.objectweb.asm.Opcodes; 18 19 /** 20 * Constants and utilities for byte code instrumentation. 21 */ 22 public final class InstrSupport { 23 InstrSupport()24 private InstrSupport() { 25 } 26 27 /** ASM API version */ 28 public static final int ASM_API_VERSION = Opcodes.ASM5; 29 30 // === Data Field === 31 32 /** 33 * Name of the field that stores coverage information of a class. 34 */ 35 public static final String DATAFIELD_NAME = "$jacocoData"; 36 37 /** 38 * Access modifiers of the field that stores coverage information of a 39 * class. 40 * 41 * According to Java Virtual Machine Specification <a href= 42 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.putstatic"> 43 * §6.5.putstatic</a> this field must not be final: 44 * 45 * <blockquote> 46 * <p> 47 * if the field is final, it must be declared in the current class, and the 48 * instruction must occur in the {@code <clinit>} method of the current 49 * class. 50 * </p> 51 * </blockquote> 52 */ 53 public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC 54 | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT; 55 56 /** 57 * Access modifiers of the field that stores coverage information of a Java 58 * 8 interface. 59 * 60 * According to Java Virtual Machine Specification <a href= 61 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5-200-A.3"> 62 * §4.5</a>: 63 * 64 * <blockquote> 65 * <p> 66 * Fields of interfaces must have their ACC_PUBLIC, ACC_STATIC, and 67 * ACC_FINAL flags set; they may have their ACC_SYNTHETIC flag set and must 68 * not have any of the other flags. 69 * </p> 70 * </blockquote> 71 */ 72 public static final int DATAFIELD_INTF_ACC = Opcodes.ACC_SYNTHETIC 73 | Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; 74 75 /** 76 * Data type of the field that stores coverage information for a class ( 77 * <code>boolean[]</code>). 78 */ 79 public static final String DATAFIELD_DESC = "[Z"; 80 81 // === Init Method === 82 83 /** 84 * Name of the initialization method. 85 */ 86 public static final String INITMETHOD_NAME = "$jacocoInit"; 87 88 /** 89 * Descriptor of the initialization method. 90 */ 91 public static final String INITMETHOD_DESC = "()[Z"; 92 93 /** 94 * Access modifiers of the initialization method. 95 */ 96 public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC 97 | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; 98 99 /** 100 * Name of the interface initialization method. 101 * 102 * According to Java Virtual Machine Specification <a href= 103 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9-200"> 104 * §2.9</a>: 105 * 106 * <blockquote> 107 * <p> 108 * A class or interface has at most one class or interface initialization 109 * method and is initialized by invoking that method. The initialization 110 * method of a class or interface has the special name {@code <clinit>}, 111 * takes no arguments, and is void. 112 * </p> 113 * <p> 114 * Other methods named {@code <clinit>} in a class file are of no 115 * consequence. They are not class or interface initialization methods. They 116 * cannot be invoked by any Java Virtual Machine instruction and are never 117 * invoked by the Java Virtual Machine itself. 118 * </p> 119 * <p> 120 * In a class file whose version number is 51.0 or above, the method must 121 * additionally have its ACC_STATIC flag set in order to be the class or 122 * interface initialization method. 123 * </p> 124 * <p> 125 * This requirement was introduced in Java SE 7. In a class file whose 126 * version number is 50.0 or below, a method named {@code <clinit>} that is 127 * void and takes no arguments is considered the class or interface 128 * initialization method regardless of the setting of its ACC_STATIC flag. 129 * </p> 130 * </blockquote> 131 * 132 * And <a href= 133 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6-200-A.6"> 134 * §4.6</a>: 135 * 136 * <blockquote> 137 * <p> 138 * Class and interface initialization methods are called implicitly by the 139 * Java Virtual Machine. The value of their access_flags item is ignored 140 * except for the setting of the ACC_STRICT flag. 141 * </p> 142 * </blockquote> 143 */ 144 static final String CLINIT_NAME = "<clinit>"; 145 146 /** 147 * Descriptor of the interface initialization method. 148 * 149 * @see #CLINIT_NAME 150 */ 151 static final String CLINIT_DESC = "()V"; 152 153 /** 154 * Access flags of the interface initialization method generated by JaCoCo. 155 * 156 * @see #CLINIT_NAME 157 */ 158 static final int CLINIT_ACC = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC; 159 160 /** 161 * Ensures that the given member does not correspond to a internal member 162 * created by the instrumentation process. This would mean that the class is 163 * already instrumented. 164 * 165 * @param member 166 * name of the member to check 167 * @param owner 168 * name of the class owning the member 169 * @throws IllegalStateException 170 * thrown if the member has the same name than the 171 * instrumentation member 172 */ assertNotInstrumented(final String member, final String owner)173 public static void assertNotInstrumented(final String member, 174 final String owner) throws IllegalStateException { 175 if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) { 176 throw new IllegalStateException(format( 177 "Class %s is already instrumented.", owner)); 178 } 179 } 180 181 /** 182 * Generates the instruction to push the given int value on the stack. 183 * Implementation taken from 184 * {@link org.objectweb.asm.commons.GeneratorAdapter#push(int)}. 185 * 186 * @param mv 187 * visitor to emit the instruction 188 * @param value 189 * the value to be pushed on the stack. 190 */ push(final MethodVisitor mv, final int value)191 public static void push(final MethodVisitor mv, final int value) { 192 if (value >= -1 && value <= 5) { 193 mv.visitInsn(Opcodes.ICONST_0 + value); 194 } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 195 mv.visitIntInsn(Opcodes.BIPUSH, value); 196 } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 197 mv.visitIntInsn(Opcodes.SIPUSH, value); 198 } else { 199 mv.visitLdcInsn(Integer.valueOf(value)); 200 } 201 } 202 203 } 204