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