ExecutionDataAccess.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 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 * $Id: $
12 *******************************************************************************/
13package org.jacoco.core.runtime;
14
15import org.jacoco.core.data.ExecutionDataStore;
16import org.jacoco.core.instr.InstrSupport;
17import org.objectweb.asm.MethodVisitor;
18import org.objectweb.asm.Opcodes;
19
20/**
21 * This class implements the access from instrumented classes to execution data
22 * storage in the runtime. Instead of directly referencing JaCoCo implementation
23 * classes the access is decoupled through a JRE API. This avoids dependencies
24 * from instrumented classes on JaCoCo APIs.
25 *
26 * The JRE interface method used is {@link Object#equals(Object)} where the
27 * passed argument is an {@link Object} array containing the class id (
28 * {@link Long}), the class vm name ({@link String}) and the probe count (
29 * {@link Integer}). After the method call the probe array instance is stored in
30 * the first slot of the {@link Object} array.
31 *
32 * @author Marc R. Hoffmann
33 * @version $Revision: $
34 */
35class ExecutionDataAccess {
36
37 private final ExecutionDataStore store;
38
39 ExecutionDataAccess(final ExecutionDataStore store) {
40 this.store = store;
41 }
42
43 /**
44 * Retrieves the execution probe array for a given class. The passed
45 * {@link Object} array instance is used for parameters and the return value
46 * as follows. Call parameters:
47 *
48 * <ul>
49 * <li>args[0]: class id ({@link Long})
50 * <li>args[1]: vm class name ({@link String})
51 * <li>args[2]: probe count ({@link Integer})
52 * </ul>
53 *
54 * Return value:
55 *
56 * <ul>
57 * <li>args[0]: probe array (<code>boolean[]</code>)
58 * </ul>
59 *
60 * @param args
61 * parameter array of length 3
62 */
63 public void getExecutionData(final Object[] args) {
64 final Long classid = (Long) args[0];
65 final String name = (String) args[1];
66 final int probecount = ((Integer) args[2]).intValue();
67 synchronized (store) {
68 args[0] = store.getData(classid, name, probecount);
69 }
70 }
71
72 /**
73 * Generates code that creates the argument array for the
74 * <code>getExecutionData()</code> method. The array instance is left on the
75 * operand stack. The generated code requires a stack size of 5.
76 *
77 * @param classid
78 * class identifier
79 * @param classname
80 * VM class name
81 * @param probecount
82 * probe count for this class
83 * @param mv
84 * visitor to emit generated code
85 */
86 public static void generateArgumentArray(final long classid,
87 final String classname, final int probecount, final MethodVisitor mv) {
88 mv.visitInsn(Opcodes.ICONST_3);
89 mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
90
91 // Class Id:
92 mv.visitInsn(Opcodes.DUP);
93 mv.visitInsn(Opcodes.ICONST_0);
94 mv.visitLdcInsn(Long.valueOf(classid));
95 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf",
96 "(J)Ljava/lang/Long;");
97 mv.visitInsn(Opcodes.AASTORE);
98
99 // Class Name:
100 mv.visitInsn(Opcodes.DUP);
101 mv.visitInsn(Opcodes.ICONST_1);
102 mv.visitLdcInsn(classname);
103 mv.visitInsn(Opcodes.AASTORE);
104
105 // Probe Count:
106 mv.visitInsn(Opcodes.DUP);
107 mv.visitInsn(Opcodes.ICONST_2);
108 InstrSupport.push(mv, probecount);
109 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer",
110 "valueOf", "(I)Ljava/lang/Integer;");
111 mv.visitInsn(Opcodes.AASTORE);
112 }
113
114 /**
115 * Generates the code that calls the runtime data access through the JRE API
116 * method {@link Object#equals(Object)}. The code pops a {@link Object}
117 * instance from the stack and pushes the probe array of type
118 * <code>boolean[]</code> on the operand stack. The generated code requires
119 * a stack size of 6.
120 *
121 * @param classid
122 * @param classname
123 * @param probecount
124 * @param mv
125 */
126 public static void generateAccessCall(final long classid,
127 final String classname, final int probecount, final MethodVisitor mv) {
128 // stack[0]: Ljava/lang/Object;
129
130 generateArgumentArray(classid, classname, probecount, mv);
131
132 // stack[1]: [Ljava/lang/Object;
133 // stack[0]: Ljava/lang/Object;
134
135 mv.visitInsn(Opcodes.DUP_X1);
136
137 // stack[2]: [Ljava/lang/Object;
138 // stack[1]: Ljava/lang/Object;
139 // stack[0]: [Ljava/lang/Object;
140
141 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals",
142 "(Ljava/lang/Object;)Z");
143 mv.visitInsn(Opcodes.POP);
144
145 // stack[0]: [Ljava/lang/Object;
146
147 mv.visitInsn(Opcodes.ICONST_0);
148 mv.visitInsn(Opcodes.AALOAD);
149
150 // stack[0]: [Z
151
152 mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC);
153 }
154
155 /**
156 * In violation of the regular semantic of {@link Object#equals(Object)}
157 * this implementation is used as the interface to the execution data store.
158 *
159 * @param args
160 * the arguments as an {@link Object} array
161 * @return has no meaning
162 */
163 @Override
164 public boolean equals(final Object args) {
165 getExecutionData((Object[]) args);
166 return false;
167 }
168
169}