1// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4package com.android.tools.r8.debug;
5
6import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
7import java.util.HashMap;
8import java.util.Map;
9import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
10import org.apache.harmony.jpda.tests.framework.jdwp.Value;
11import org.junit.Assert;
12import org.junit.Test;
13
14/**
15 * Tests local variable information.
16 */
17public class LocalsTest extends DebugTestBase {
18
19  public static final String SOURCE_FILE = "Locals.java";
20
21  @Test
22  public void testNoLocal() throws Throwable {
23    final String className = "Locals";
24    final String methodName = "noLocals";
25    runDebugTest(className,
26        breakpoint(className, methodName),
27        run(),
28        checkMethod(className, methodName),
29        checkLine(SOURCE_FILE, 8),
30        checkNoLocal(),
31        stepOver(),
32        checkMethod(className, methodName),
33        checkLine(SOURCE_FILE, 9),
34        checkNoLocal(),
35        run());
36  }
37
38  @Test
39  public void testUnusedLocal() throws Throwable {
40    final String className = "Locals";
41    final String methodName = "unusedLocals";
42    runDebugTest(className,
43        breakpoint(className, methodName),
44        run(),
45        checkMethod(className, methodName),
46        checkLine(SOURCE_FILE, 12),
47        checkNoLocal(),
48        stepOver(),
49        checkLine(SOURCE_FILE, 13),
50        checkLocal("i", Value.createInt(Integer.MAX_VALUE)),
51        run());
52  }
53
54  @Test
55  public void testConstantLocal() throws Throwable {
56    final String className = "Locals";
57    final String methodName = "constantLocals";
58    Value pValue = Value.createInt(10);
59    Value cValue = Value.createInt(5);
60    Value vValue = Value.createInt(pValue.getIntValue() + cValue.getIntValue());
61
62    runDebugTest(className,
63        breakpoint(className, methodName),
64        run(),
65        checkMethod(className, methodName),
66        checkLine(SOURCE_FILE, 17),
67        checkLocal("p", pValue),
68        stepOver(),
69        checkLine(SOURCE_FILE, 18),
70        checkLocal("p", pValue),
71        checkLocal("c", cValue),
72        stepOver(),
73        checkLine(SOURCE_FILE, 19),
74        checkLocal("p", pValue),
75        checkLocal("c", cValue),
76        checkLocal("v", vValue),
77        run());
78  }
79
80  @Test
81  public void testConstantLocalWithUpdate() throws Throwable {
82    final String className = "Locals";
83    final String methodName = "constantLocals";
84    Value pValue = Value.createInt(10);
85    Value cValue = Value.createInt(5);
86    Value newValue = Value.createInt(5);
87    Value vValue = Value.createInt(pValue.getIntValue() + newValue.getIntValue());
88
89    runDebugTest(className,
90        breakpoint(className, methodName),
91        run(),
92        checkMethod(className, methodName),
93        checkLine(SOURCE_FILE, 17),
94        checkLocal("p", pValue),
95        stepOver(),
96        checkLine(SOURCE_FILE, 18),
97        checkLocal("p", pValue),
98        checkLocal("c", cValue),
99        setLocal("c", newValue),
100        checkLocal("c", newValue),  // we should see the updated value
101        stepOver(),
102        checkLine(SOURCE_FILE, 19),
103        checkLocal("p", pValue),
104        checkLocal("c", newValue),
105        checkLocal("v", vValue),
106        run());
107  }
108
109  @Test
110  public void testZeroLocals() throws Throwable {
111    final String className = "Locals";
112    final String methodName = "zeroLocals";
113    final Value newValueForI = Value.createInt(10);
114    runDebugTest(className,
115        breakpoint(className, methodName),
116        run(),
117        checkMethod(className, methodName),
118        checkLine(SOURCE_FILE, 23),
119        checkNoLocal(),
120        stepOver(),
121        checkMethod(className, methodName),
122        checkLine(SOURCE_FILE, 24),
123        checkLocal("i", Value.createInt(0)),
124        setLocal("i", newValueForI),
125        stepOver(),
126        checkLine(SOURCE_FILE, 25),
127        checkLocal("i", newValueForI),
128        checkLocal("f", Value.createFloat(0)),
129        run());
130  }
131
132  @Test
133  public void testNoFlowOptimization() throws Throwable {
134    final String className = "Locals";
135    final String methodName = "noFlowOptimization";
136    final Value oldValueForI = Value.createInt(0);
137    final Value newValueForI = Value.createInt(10);
138    runDebugTest(className,
139        breakpoint(className, methodName),
140        run(),
141        checkMethod(className, methodName),
142        checkLine(SOURCE_FILE, 29),
143        checkNoLocal(),
144        stepOver(),
145        checkMethod(className, methodName),
146        checkLine(SOURCE_FILE, 30),
147        checkLocal("i", oldValueForI),
148        setLocal("i", newValueForI),
149        stepOver(),
150        checkLine(SOURCE_FILE, 33),
151        checkLocal("i", newValueForI),
152        run());
153  }
154
155  @Test
156  public void testInvokeRange() throws Throwable {
157    runDebugTest("Locals",
158        breakpoint("Locals", "invokeRange"),
159        run(),
160        inspect(state -> {
161          // 1st breakpoint
162          Assert.assertEquals("invokeRange", state.getMethodName());
163          Assert.assertEquals(58, state.getLineNumber());
164          state.checkLocal("a", Value.createInt(12));
165          state.checkLocal("b", Value.createInt(11));
166          state.checkLocal("c", Value.createInt(10));
167          state.checkLocal("d", Value.createInt(9));
168          state.checkLocal("e", Value.createInt(8));
169          state.checkLocal("f", Value.createInt(7));
170          state.checkLocal("g", Value.createInt(0));
171
172          FrameInspector outerFrame = state.getFrame(1);
173          for (int i = 1; i < 12; ++i) {
174            outerFrame.checkLocal("i" + i, Value.createInt(i));
175          }
176        }),
177        run(),
178        inspect(state -> {
179          // 2nd breakpoint
180          Assert.assertEquals("invokeRange", state.getMethodName());
181          Assert.assertEquals(58, state.getLineNumber());
182          state.checkLocal("a", Value.createInt(6));
183          state.checkLocal("b", Value.createInt(5));
184          state.checkLocal("c", Value.createInt(4));
185          state.checkLocal("d", Value.createInt(3));
186          state.checkLocal("e", Value.createInt(2));
187          state.checkLocal("f", Value.createInt(1));
188          state.checkLocal("g", Value.createInt(57));
189
190          FrameInspector outerFrame = state.getFrame(1);
191          for (int i = 1; i < 12; ++i) {
192            outerFrame.checkLocal("i" + i, Value.createInt(i));
193          }
194        }),
195        run(),
196        // TODO(shertz) maybe we should duplicate invokeRange to avoid this extra 'skip'.
197        // Skip last breakpoint
198        run());
199  }
200
201  @Test
202  public void testInvokeRange2() throws Throwable {
203    runDebugTest("Locals",
204        breakpoint("Locals", "reverseRange"),
205        run(),
206        inspect(state -> {
207          Assert.assertEquals("reverseRange", state.getMethodName());
208          Assert.assertEquals(54, state.getLineNumber());
209          state.checkLocal("a", Value.createInt(1));
210          state.checkLocal("b", Value.createInt(2));
211          state.checkLocal("c", Value.createInt(3));
212          state.checkLocal("d", Value.createInt(4));
213          state.checkLocal("e", Value.createInt(5));
214          state.checkLocal("f", Value.createInt(6));
215          state.checkLocal("g", Value.createInt(7));
216        }),
217        stepInto(),
218        inspect(state -> {
219          Assert.assertEquals("invokeRange", state.getMethodName());
220          Assert.assertEquals(58, state.getLineNumber());
221          state.checkLocal("a", Value.createInt(7));
222          state.checkLocal("b", Value.createInt(6));
223          state.checkLocal("c", Value.createInt(5));
224          state.checkLocal("d", Value.createInt(4));
225          state.checkLocal("e", Value.createInt(3));
226          state.checkLocal("f", Value.createInt(2));
227          state.checkLocal("g", Value.createInt(1));
228        }),
229        inspect(state -> {
230          FrameInspector outerFrame = state.getFrame(1);
231          outerFrame.checkLocal("a", Value.createInt(1));
232          outerFrame.checkLocal("b", Value.createInt(2));
233          outerFrame.checkLocal("c", Value.createInt(3));
234          outerFrame.checkLocal("d", Value.createInt(4));
235          outerFrame.checkLocal("e", Value.createInt(5));
236          outerFrame.checkLocal("f", Value.createInt(6));
237          outerFrame.checkLocal("g", Value.createInt(7));
238        }),
239        run());
240  }
241
242  @Test
243  public void testLocals_MoreThan16() throws Throwable {
244    final int minIndex = 1;
245    final int maxIndex = 16;
246    Map<String, Value> arrayLocals = new HashMap<>();
247    runDebugTest("Locals",
248        breakpoint("Locals", "breakpoint"),
249        run(),
250        inspect(state -> {
251          // 1st breakpoint: all lengthOfArray[N] are set to 0
252          FrameInspector outerFrame = state.getFrame(1);
253
254          Map<String, Value> localValues = outerFrame.getLocalValues();
255
256          for (int i = minIndex; i <= maxIndex; ++i) {
257            String varName = "lengthOfArray" + i;
258            Assert.assertTrue(localValues.containsKey(varName));
259            Assert.assertEquals(Value.createInt(0), localValues.get(varName));
260          }
261
262          // Capture IDs of arrays.
263          for (int i = minIndex; i <= maxIndex; ++i) {
264            String varName = "array" + i;
265            Assert.assertTrue(localValues.containsKey(varName));
266            arrayLocals.put(varName, localValues.get(varName));
267          }
268        }),
269        // Step out to reach next instructions in the tested method
270        stepOut(),
271        inspect(state -> {
272          Assert.assertEquals("Locals.java", state.getSourceFile());
273          Assert.assertEquals(107, state.getLineNumber());
274          // Verify that all arrays have the same value.
275          arrayLocals.forEach((name, value) -> state.checkLocal(name, value));
276        }),
277        // Step instruction by instruction to ensure all locals previously declared are safe.
278        stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, state -> {
279          final String sourceFile = state.getSourceFile();
280          final int lineNumber = state.getLineNumber();
281          arrayLocals.forEach((name, value) -> state.checkLocal(name, value));
282          // Stop when we reach the expected line.
283          return lineNumber == 125 && sourceFile.equals("Locals.java");
284        }),
285        run());
286  }
287
288  @Test
289  public void testInvokeRangeLong() throws Throwable {
290    final int initialValueOfX = 21;
291    final long expectedValueOfL = (long) initialValueOfX * 2;
292    final int expectedValueOfX = (int) expectedValueOfL / initialValueOfX;
293    runDebugTest("Locals",
294        breakpoint("Locals", "invokerangeLong"),
295        run(),
296        inspect(state -> {
297          FrameInspector outerFrame = state.getFrame(1);
298          Map<String, Value> values = outerFrame.getLocalValues();
299          Assert.assertTrue("No variable 'x'", values.containsKey("x"));
300          Assert.assertTrue("No variable 'obj'", values.containsKey("obj"));
301          Assert.assertTrue("No variable 'l'", values.containsKey("l"));
302
303          // 'x' is an int
304          Value valueOfX = values.get("x");
305          Assert.assertEquals(Tag.INT_TAG, valueOfX.getTag());
306          Assert.assertEquals(Value.createInt(expectedValueOfX), valueOfX);
307
308          // 'obj' is an Object (Integer).
309          Value valueOfObj = values.get("obj");
310          Assert.assertEquals(Tag.OBJECT_TAG, valueOfObj.getTag());
311
312          // 'l' is a long.
313          Value valueOfL = values.get("l");
314          Assert.assertEquals(Tag.LONG_TAG, valueOfL.getTag());
315          Assert.assertEquals(Value.createLong(expectedValueOfL), valueOfL);
316        }),
317        run());
318  }
319
320}
321