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