• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
3  * This program and the accompanying materials are made available under
4  * the terms of the Eclipse Public License 2.0 which is available at
5  * http://www.eclipse.org/legal/epl-2.0
6  *
7  * SPDX-License-Identifier: EPL-2.0
8  *
9  * Contributors:
10  *    Marc R. Hoffmann - initial API and implementation
11  *
12  *******************************************************************************/
13 package org.jacoco.agent.rt.internal;
14 
15 import java.lang.instrument.Instrumentation;
16 import java.util.Collections;
17 import java.util.Map;
18 import java.util.Set;
19 
20 import org.jacoco.core.runtime.AgentOptions;
21 import org.jacoco.core.runtime.IRuntime;
22 import org.jacoco.core.runtime.InjectedClassRuntime;
23 import org.jacoco.core.runtime.ModifiedSystemClassRuntime;
24 
25 /**
26  * The agent which is referred as the <code>Premain-Class</code>. The agent
27  * configuration is provided with the agent parameters in the command line.
28  */
29 public final class PreMain {
30 
PreMain()31 	private PreMain() {
32 		// no instances
33 	}
34 
35 	/**
36 	 * This method is called by the JVM to initialize Java agents.
37 	 *
38 	 * @param options
39 	 *            agent options
40 	 * @param inst
41 	 *            instrumentation callback provided by the JVM
42 	 * @throws Exception
43 	 *             in case initialization fails
44 	 */
premain(final String options, final Instrumentation inst)45 	public static void premain(final String options, final Instrumentation inst)
46 			throws Exception {
47 
48 		final AgentOptions agentOptions = new AgentOptions(options);
49 
50 		final Agent agent = Agent.getInstance(agentOptions);
51 
52 		final IRuntime runtime = createRuntime(inst);
53 		runtime.startup(agent.getData());
54 		inst.addTransformer(new CoverageTransformer(runtime, agentOptions,
55 				IExceptionLogger.SYSTEM_ERR));
56 	}
57 
createRuntime(final Instrumentation inst)58 	private static IRuntime createRuntime(final Instrumentation inst)
59 			throws Exception {
60 
61 		if (redefineJavaBaseModule(inst)) {
62 			return new InjectedClassRuntime(Object.class, "$JaCoCo");
63 		}
64 
65 		return ModifiedSystemClassRuntime.createFor(inst,
66 				"java/lang/UnknownError");
67 	}
68 
69 	/**
70 	 * Opens {@code java.base} module for {@link InjectedClassRuntime} when
71 	 * executed on Java 9 JREs or higher.
72 	 *
73 	 * @return <code>true</code> when running on Java 9 or higher,
74 	 *         <code>false</code> otherwise
75 	 * @throws Exception
76 	 *             if unable to open
77 	 */
redefineJavaBaseModule( final Instrumentation instrumentation)78 	private static boolean redefineJavaBaseModule(
79 			final Instrumentation instrumentation) throws Exception {
80 		try {
81 			Class.forName("java.lang.Module");
82 		} catch (final ClassNotFoundException e) {
83 			return false;
84 		}
85 
86 		Instrumentation.class.getMethod("redefineModule", //
87 				Class.forName("java.lang.Module"), //
88 				Set.class, //
89 				Map.class, //
90 				Map.class, //
91 				Set.class, //
92 				Map.class //
93 		).invoke(instrumentation, // instance
94 				getModule(Object.class), // module
95 				Collections.emptySet(), // extraReads
96 				Collections.emptyMap(), // extraExports
97 				Collections.singletonMap("java.lang",
98 						Collections.singleton(
99 								getModule(InjectedClassRuntime.class))), // extraOpens
100 				Collections.emptySet(), // extraUses
101 				Collections.emptyMap() // extraProvides
102 		);
103 		return true;
104 	}
105 
106 	/**
107 	 * @return {@code cls.getModule()}
108 	 */
getModule(final Class<?> cls)109 	private static Object getModule(final Class<?> cls) throws Exception {
110 		return Class.class //
111 				.getMethod("getModule") //
112 				.invoke(cls);
113 	}
114 
115 }
116