1/******************************************************************************* 2 * Copyright (c) 2009, 2015 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 *******************************************************************************/ 12package org.jacoco.core.internal.analysis; 13 14import org.jacoco.core.analysis.IMethodCoverage; 15import org.jacoco.core.internal.flow.ClassProbesVisitor; 16import org.jacoco.core.internal.flow.MethodProbesVisitor; 17import org.jacoco.core.internal.instr.InstrSupport; 18import org.objectweb.asm.FieldVisitor; 19import org.objectweb.asm.Opcodes; 20 21/** 22 * Analyzes the structure of a class. 23 */ 24public class ClassAnalyzer extends ClassProbesVisitor { 25 26 private final long classid; 27 private final boolean noMatch; 28 private final boolean[] probes; 29 private final StringPool stringPool; 30 31 private ClassCoverageImpl coverage; 32 33 /** 34 * Creates a new analyzer that builds coverage data for a class. 35 * 36 * @param classid 37 * id of the class 38 * @param noMatch 39 * <code>true</code> if class id does not match with execution 40 * data 41 * @param probes 42 * execution data for this class or <code>null</code> 43 * @param stringPool 44 * shared pool to minimize the number of {@link String} instances 45 */ 46 public ClassAnalyzer(final long classid, final boolean noMatch, 47 final boolean[] probes, final StringPool stringPool) { 48 this.classid = classid; 49 this.noMatch = noMatch; 50 this.probes = probes; 51 this.stringPool = stringPool; 52 } 53 54 /** 55 * Returns the coverage data for this class after this visitor has been 56 * processed. 57 * 58 * @return coverage data for this class 59 */ 60 public ClassCoverageImpl getCoverage() { 61 return coverage; 62 } 63 64 @Override 65 public void visit(final int version, final int access, final String name, 66 final String signature, final String superName, 67 final String[] interfaces) { 68 this.coverage = new ClassCoverageImpl(stringPool.get(name), classid, 69 noMatch, stringPool.get(signature), stringPool.get(superName), 70 stringPool.get(interfaces)); 71 } 72 73 @Override 74 public void visitSource(final String source, final String debug) { 75 this.coverage.setSourceFileName(stringPool.get(source)); 76 } 77 78 @Override 79 public MethodProbesVisitor visitMethod(final int access, final String name, 80 final String desc, final String signature, final String[] exceptions) { 81 82 InstrSupport.assertNotInstrumented(name, coverage.getName()); 83 84 if (isMethodFiltered(access, name)) { 85 return null; 86 } 87 88 return new MethodAnalyzer(stringPool.get(name), stringPool.get(desc), 89 stringPool.get(signature), probes) { 90 @Override 91 public void visitEnd() { 92 super.visitEnd(); 93 final IMethodCoverage methodCoverage = getCoverage(); 94 if (methodCoverage.getInstructionCounter().getTotalCount() > 0) { 95 // Only consider methods that actually contain code 96 coverage.addMethod(methodCoverage); 97 } 98 } 99 }; 100 } 101 102 // TODO: Use filter hook in future 103 private boolean isMethodFiltered(final int access, final String name) { 104 return (access & Opcodes.ACC_SYNTHETIC) != 0 105 && !name.startsWith("lambda$"); 106 } 107 108 @Override 109 public FieldVisitor visitField(final int access, final String name, 110 final String desc, final String signature, final Object value) { 111 InstrSupport.assertNotInstrumented(name, coverage.getName()); 112 return super.visitField(access, name, desc, signature, value); 113 } 114 115 @Override 116 public void visitTotalProbeCount(final int count) { 117 // nothing to do 118 } 119 120} 121