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