1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/*******************************************************************************
2b9d1b54e300318b470d9fedccc69d75187016444Evgeny Mandrikov * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
3e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * All rights reserved. This program and the accompanying materials
4e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * are made available under the terms of the Eclipse Public License v1.0
5e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * which accompanies this distribution, and is available at
6e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * http://www.eclipse.org/legal/epl-v10.html
7e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
8e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Contributors:
9e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *    Marc R. Hoffmann - initial API and implementation
10e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
11e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *******************************************************************************/
12ae1034c608eeca9765a43bec34bcb8e5bf23eaffMarc R. Hoffmannpackage org.jacoco.agent.rt.internal;
13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.ClassFileTransformer;
15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.IllegalClassFormatException;
16bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmannimport java.security.CodeSource;
17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.security.ProtectionDomain;
18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.instr.Instrumenter;
20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.AgentOptions;
21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.IRuntime;
22e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.WildcardMatcher;
23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/**
25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Class file transformer to instrument classes for code coverage analysis.
26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */
27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class CoverageTransformer implements ClassFileTransformer {
28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static final String AGENT_PREFIX;
30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	static {
32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final String name = CoverageTransformer.class.getName();
33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		AGENT_PREFIX = toVMName(name.substring(0, name.lastIndexOf('.')));
34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final Instrumenter instrumenter;
37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final IExceptionLogger logger;
39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher includes;
41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher excludes;
43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher exclClassloader;
45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final ClassFileDumper classFileDumper;
47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
4826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	private final boolean inclBootstrapClasses;
4926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann
5026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	private final boolean inclNoLocationClasses;
51310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann
52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * New transformer with the given delegates.
54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param runtime
56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            coverage runtime
57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param options
58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            configuration options for the generator
59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param logger
60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            logger for exceptions during instrumentation
61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public CoverageTransformer(final IRuntime runtime,
63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final AgentOptions options, final IExceptionLogger logger) {
64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.instrumenter = new Instrumenter(runtime);
65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.logger = logger;
66e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		// Class names will be reported in VM notation:
678bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		includes = new WildcardMatcher(toVMName(options.getIncludes()));
688bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		excludes = new WildcardMatcher(toVMName(options.getExcludes()));
698bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		exclClassloader = new WildcardMatcher(options.getExclClassloader());
70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		classFileDumper = new ClassFileDumper(options.getClassDumpDir());
7126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		inclBootstrapClasses = options.getInclBootstrapClasses();
7226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		inclNoLocationClasses = options.getInclNoLocationClasses();
73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public byte[] transform(final ClassLoader loader, final String classname,
76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final Class<?> classBeingRedefined,
77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final ProtectionDomain protectionDomain,
78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final byte[] classfileBuffer) throws IllegalClassFormatException {
79e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
80bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		// We do not support class retransformation:
8182b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann		if (classBeingRedefined != null) {
82bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann			return null;
83bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		}
84bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann
8526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		if (!filter(loader, classname, protectionDomain)) {
86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return null;
87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		try {
90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			classFileDumper.dump(classname, classfileBuffer);
91ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			return instrumenter.instrument(classfileBuffer, classname);
92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		} catch (final Exception ex) {
93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final IllegalClassFormatException wrapper = new IllegalClassFormatException(
94ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann					ex.getMessage());
95c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			wrapper.initCause(ex);
96e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			// Report this, as the exception is ignored by the JVM:
97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			logger.logExeption(wrapper);
98c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			throw wrapper;
99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Checks whether this class should be instrumented.
104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param loader
106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            loader for the class
107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param classname
108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            VM name of the class to check
10926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 * @param protectionDomain
11026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 *            protection domain for the class
111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return <code>true</code> if the class should be instrumented
112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
11326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	boolean filter(final ClassLoader loader, final String classname,
11426daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			final ProtectionDomain protectionDomain) {
115bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		if (loader == null) {
11626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			if (!inclBootstrapClasses) {
117310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann				return false;
118310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			}
119bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		} else {
12026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			if (!inclNoLocationClasses && !hasSourceLocation(protectionDomain)) {
12126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann				return false;
12226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			}
123310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			if (exclClassloader.matches(loader.getClass().getName())) {
124310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann				return false;
125310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			}
126310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann		}
127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
128310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann		return !classname.startsWith(AGENT_PREFIX) &&
129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		includes.matches(classname) &&
131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		!excludes.matches(classname);
133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
134e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
13526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	/**
13626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 * Checks whether this protection domain is associated with a source
13726daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 * location.
13826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 *
13926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 * @param protectionDomain
14026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 *            protection domain to check (or <code>null</code>)
14126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 * @return <code>true</code> if a source location is defined
14226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	 */
14326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	private boolean hasSourceLocation(final ProtectionDomain protectionDomain) {
14426daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		if (protectionDomain == null) {
14526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			return false;
14626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		}
14726daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		final CodeSource codeSource = protectionDomain.getCodeSource();
14826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		if (codeSource == null) {
14926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann			return false;
15026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		}
15126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann		return codeSource.getLocation() != null;
15226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann	}
15326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann
154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static String toVMName(final String srcName) {
155e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return srcName.replace('.', '/');
156e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
157e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
158e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov}
159