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 *******************************************************************************/
12e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpackage org.jacoco.core.runtime;
13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport static java.lang.String.format;
15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
16e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.ClassFileTransformer;
17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.IllegalClassFormatException;
18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.Instrumentation;
19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.reflect.Field;
20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.security.ProtectionDomain;
21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
229f4ef8dcffb3acbcd066ec073d87de65eeb06b48Marc R. Hoffmannimport org.jacoco.core.JaCoCo;
23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassReader;
24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassVisitor;
25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassWriter;
26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.MethodVisitor;
27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.Opcodes;
28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/**
30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * This {@link IRuntime} implementation works with a modified system class. A
31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * new static method is added to a bootstrap class that will be used by
32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * instrumented classes. As the system class itself needs to be instrumented
33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * this runtime requires a Java agent.
34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */
35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class ModifiedSystemClassRuntime extends AbstractRuntime {
36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static final String ACCESS_FIELD_TYPE = "Ljava/lang/Object;";
38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final Class<?> systemClass;
40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final String systemClassName;
42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final String accessFieldName;
44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Creates a new runtime based on the given class and members.
47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
48e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param systemClass
49e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            system class that contains the execution data
50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param accessFieldName
51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            name of the public static runtime access field
52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public ModifiedSystemClassRuntime(final Class<?> systemClass,
55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final String accessFieldName) {
56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		super();
57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.systemClass = systemClass;
58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.systemClassName = systemClass.getName().replace('.', '/');
59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.accessFieldName = accessFieldName;
60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
6227e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann	@Override
6327e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann	public void startup(final RuntimeData data) throws Exception {
6427e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann		super.startup(data);
65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final Field field = systemClass.getField(accessFieldName);
6627e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann		field.set(null, data);
67e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
69e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public void shutdown() {
70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		// nothing to do
71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public int generateDataAccessor(final long classid, final String classname,
74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final int probecount, final MethodVisitor mv) {
75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		mv.visitFieldInsn(Opcodes.GETSTATIC, systemClassName, accessFieldName,
77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				ACCESS_FIELD_TYPE);
78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
7927e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann		RuntimeData.generateAccessCall(classid, classname, probecount, mv);
80e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
81e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return 6;
82e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
83e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
84e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
85e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Creates a new {@link ModifiedSystemClassRuntime} using the given class as
86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * the data container. Members are creates with internal default names. The
87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * given class must not have been loaded before by the agent.
88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param inst
90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            instrumentation interface
91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param className
92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            VM name of the class to use
93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return new runtime instance
94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws ClassNotFoundException
96e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *             id the given class can not be found
97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public static IRuntime createFor(final Instrumentation inst,
99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final String className) throws ClassNotFoundException {
100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return createFor(inst, className, "$jacocoAccess");
101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Creates a new {@link ModifiedSystemClassRuntime} using the given class as
105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * the data container. The given class must not have been loaded before by
106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * the agent.
107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param inst
109e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            instrumentation interface
110e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param className
111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            VM name of the class to use
112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param accessFieldName
113e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            name of the added runtime access field
114e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return new runtime instance
115e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
116e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws ClassNotFoundException
117e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *             id the given class can not be found
118e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
119e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public static IRuntime createFor(final Instrumentation inst,
120e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final String className, final String accessFieldName)
121e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			throws ClassNotFoundException {
122e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final ClassFileTransformer transformer = new ClassFileTransformer() {
123e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			public byte[] transform(final ClassLoader loader,
124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					final String name, final Class<?> classBeingRedefined,
125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					final ProtectionDomain protectionDomain, final byte[] source)
126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					throws IllegalClassFormatException {
127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				if (name.equals(className)) {
128e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					return instrument(source, accessFieldName);
129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				}
130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				return null;
131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		};
133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		inst.addTransformer(transformer);
134e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final Class<?> clazz = Class.forName(className.replace('/', '.'));
135e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		inst.removeTransformer(transformer);
136e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		try {
137e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			clazz.getField(accessFieldName);
138e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		} catch (final NoSuchFieldException e) {
139e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			throw new RuntimeException(format(
140e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					"Class %s could not be instrumented.", className), e);
141e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
142e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return new ModifiedSystemClassRuntime(clazz, accessFieldName);
143e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
144e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
145e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
146e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Adds the static access method and data field to the given class
147e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * definition.
148e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
149e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param source
150e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            class definition source
151e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param accessFieldName
152e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            name of the runtime access field
153e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return instrumented version with added members
154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
155e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public static byte[] instrument(final byte[] source,
156e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final String accessFieldName) {
157e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final ClassReader reader = new ClassReader(source);
158e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final ClassWriter writer = new ClassWriter(reader, 0);
1599f4ef8dcffb3acbcd066ec073d87de65eeb06b48Marc R. Hoffmann		reader.accept(new ClassVisitor(JaCoCo.ASM_API_VERSION, writer) {
160e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
161e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			@Override
162e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			public void visitEnd() {
163e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				createDataField(cv, accessFieldName);
164e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				super.visitEnd();
165e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
166e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
167e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}, ClassReader.EXPAND_FRAMES);
168e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return writer.toByteArray();
169e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
170e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
171e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static void createDataField(final ClassVisitor visitor,
172e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final String dataField) {
173e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
174e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				| Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT, dataField,
175e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				ACCESS_FIELD_TYPE, null, null);
176e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
177e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
178e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov}
179