1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/*******************************************************************************
2398ee59bebad6835dab57b60157eff16d511709eMarc R. Hoffmann * Copyright (c) 2009, 2015 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
48310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann	private final boolean includeBootstrapClasses;
49310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann
50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * New transformer with the given delegates.
52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param runtime
54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            coverage runtime
55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param options
56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            configuration options for the generator
57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param logger
58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            logger for exceptions during instrumentation
59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public CoverageTransformer(final IRuntime runtime,
61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final AgentOptions options, final IExceptionLogger logger) {
62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.instrumenter = new Instrumenter(runtime);
63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.logger = logger;
64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		// Class names will be reported in VM notation:
658bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		includes = new WildcardMatcher(toVMName(options.getIncludes()));
668bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		excludes = new WildcardMatcher(toVMName(options.getExcludes()));
678bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann		exclClassloader = new WildcardMatcher(options.getExclClassloader());
68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		classFileDumper = new ClassFileDumper(options.getClassDumpDir());
691d8389bf86f8a49d31bb5182b3ec1592e2f7289aMarc R. Hoffmann		includeBootstrapClasses = options.getInclBootstrapClasses();
70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public byte[] transform(final ClassLoader loader, final String classname,
73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final Class<?> classBeingRedefined,
74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final ProtectionDomain protectionDomain,
75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final byte[] classfileBuffer) throws IllegalClassFormatException {
76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
77bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		// We do not support class retransformation:
7882b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann		if (classBeingRedefined != null) {
79bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann			return null;
80bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		}
81bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann
82bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		// We exclude dynamically created non-bootstrap classes.
83bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		if (loader != null && !hasSourceLocation(protectionDomain)) {
8482b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann			return null;
8582b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann		}
8682b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann
87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		if (!filter(loader, classname)) {
88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return null;
89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		try {
92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			classFileDumper.dump(classname, classfileBuffer);
93ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			return instrumenter.instrument(classfileBuffer, classname);
94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		} catch (final Exception ex) {
95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final IllegalClassFormatException wrapper = new IllegalClassFormatException(
96ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann					ex.getMessage());
97c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			wrapper.initCause(ex);
98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			// Report this, as the exception is ignored by the JVM:
99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			logger.logExeption(wrapper);
100c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			throw wrapper;
101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
105bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 * Checks whether this protection domain is associated with a source
106bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 * location.
107bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 *
108bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 * @param protectionDomain
109bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 *            protection domain to check (or <code>null</code>)
110bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 * @return <code>true</code> if a source location is defined
111bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	 */
112bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	boolean hasSourceLocation(final ProtectionDomain protectionDomain) {
113bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		if (protectionDomain == null) {
114bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann			return false;
115bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		}
116bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		final CodeSource codeSource = protectionDomain.getCodeSource();
117bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		if (codeSource == null) {
118bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann			return false;
119bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		}
120bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		return codeSource.getLocation() != null;
121bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	}
122bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann
123bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	/**
124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Checks whether this class should be instrumented.
125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param loader
127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            loader for the class
128e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param classname
129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            VM name of the class to check
130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return <code>true</code> if the class should be instrumented
131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
132bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann	boolean filter(final ClassLoader loader, final String classname) {
133bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		if (loader == null) {
134bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann			if (!includeBootstrapClasses) {
135310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann				return false;
136310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			}
137bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann		} else {
138310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			if (exclClassloader.matches(loader.getClass().getName())) {
139310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann				return false;
140310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann			}
141310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann		}
142e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
143310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann		return !classname.startsWith(AGENT_PREFIX) &&
144e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
145e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		includes.matches(classname) &&
146e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
147e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		!excludes.matches(classname);
148e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
149e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
150e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static String toVMName(final String srcName) {
151e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return srcName.replace('.', '/');
152e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
153e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov}
155