1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 */
18
19package org.apache.harmony.jpda.tests.jdwp.StackFrame;
20
21import org.apache.harmony.jpda.tests.framework.TestErrorException;
22import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
23import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
24import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
25import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
26import org.apache.harmony.jpda.tests.framework.jdwp.Value;
27import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Base class for testing StackFrame.GetValues and StackFrame.SetValues commands.
34 */
35public class JDWPStackFrameAccessTest extends JDWPStackFrameTestCase {
36    @Override
37    protected final String getDebuggeeClassName() {
38        return StackTrace002Debuggee.class.getName();
39    }
40
41    static class VariableInfo {
42        /**
43         * The name of the local variable to test.
44         */
45        private final String variableName;
46
47        /**
48         * The initial value of the tested local variable, before we change its value.
49         */
50        private final Value valueOnFirstSuspension;
51
52        /**
53         * The new value of the tested local variable that we set during the test.
54         */
55        private final Value valueToSet;
56
57        /**
58         * The new value of the tested local variable, after change its value.
59         */
60        private final Value valueOnSecondSuspension;
61
62        VariableInfo(String variableName, Value initialValue, Value setValue, Value newValue) {
63            this.variableName = variableName;
64            this.valueOnFirstSuspension = initialValue;
65            this.valueToSet = setValue;
66            this.valueOnSecondSuspension = newValue;
67        }
68
69        public String getVariableName() {
70            return variableName;
71        }
72
73        public Value getValueOnFirstSuspension() {
74            return valueOnFirstSuspension;
75        }
76
77        public Value getValueToSet() {
78            return valueToSet;
79        }
80
81        public Value getValueOnSecondSuspension() {
82            return valueOnSecondSuspension;
83        }
84    }
85
86    static class MethodInfo {
87        private final String methodName;
88        private final List<VariableInfo> variables = new ArrayList<VariableInfo>();
89
90        MethodInfo(String methodName) {
91            this.methodName = methodName;
92        }
93
94        public String getMethodName() {
95            return methodName;
96        }
97
98        public List<? extends VariableInfo> getVariables() {
99            return variables;
100        }
101
102        public void addVariable(String variableName, Value initialValue, Value setValue,
103                                Value newValue) {
104            variables.add(new VariableInfo(variableName, initialValue, setValue, newValue));
105        }
106
107        public void addVariable(String variableName, Value initialValue, Value setValue) {
108            Value newValue;
109            if (setValue == null) {
110                // We test GetValue so we expect to read the same value on second suspension.
111                newValue = initialValue;
112            } else {
113                // We test SetValue so we expect to read the new value on second suspension.
114                newValue = setValue;
115            }
116            variables.add(new VariableInfo(variableName, initialValue, setValue, newValue));
117        }
118
119        public void addVariable(String variableName, Value initialValue) {
120            addVariable(variableName, initialValue, null);
121        }
122    }
123
124    static class StackFrameTester {
125        private final String signalValue;
126        private final List<MethodInfo> testedMethods = new ArrayList<MethodInfo>();
127
128        public StackFrameTester(String signalValue) {
129            this.signalValue = signalValue;
130        }
131
132        public String getSignalValue() {
133            return signalValue;
134        }
135
136        public List<? extends MethodInfo> getTestedMethods() {
137            return testedMethods;
138        }
139
140        public MethodInfo addTestMethod(String methodName) {
141            MethodInfo methodInfo = new MethodInfo(methodName);
142            testedMethods.add(methodInfo);
143            return methodInfo;
144        }
145    }
146
147    @Override
148    protected void internalSetUp() throws Exception {
149        super.internalSetUp();
150
151        printTestLog("STARTED");
152
153        // Wait for debuggee to start.
154        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
155    }
156
157    @Override
158    protected void internalTearDown() {
159        printTestLog("FINISHED");
160
161        super.internalTearDown();
162    }
163
164    protected void runStackFrameTest(StackFrameTester tester, MethodInfo suspensionMethodInfo) {
165        // Get variable information.
166        long classID = getClassIDBySignature(getDebuggeeClassSignature());
167
168        // Signal debuggee with a custom message to execute the right method.
169        synchronizer.sendMessage(tester.getSignalValue());
170
171        // Wait for 1st suspension.
172        long eventThreadID = suspendDebuggee();
173
174        // Check every local variable of every method.
175        checkStackFrame(classID, eventThreadID, tester, suspensionMethodInfo, true);
176
177        // Resume debuggee.
178        resumeTest(eventThreadID);
179
180        // Wait for 2nd suspension.
181        eventThreadID = suspendDebuggee();
182
183        // Check every local variable of every method.
184        checkStackFrame(classID, eventThreadID, tester, suspensionMethodInfo, false);
185
186        resumeTest(eventThreadID);
187    }
188
189    private long suspendDebuggee() {
190        String threadName = synchronizer.receiveMessage();
191        long threadId = getThreadIdFromName(threadName);
192        // We need the thread to be suspended so we suspend it explicitly now.
193        debuggeeWrapper.vmMirror.suspendThread(threadId);
194        return threadId;
195    }
196
197    private void resumeTest(long threadId) {
198        // We suspended the thread so let's resume it before sending the signal.
199        debuggeeWrapper.vmMirror.resumeThread(threadId);
200        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
201    }
202
203    private long getThreadIdFromName(String threadName) {
204        long[] allThreadIds = jdwpGetAllThreads();
205        for (long threadId : allThreadIds) {
206            String currentThreadName = jdwpGetThreadName(threadId);
207            if (threadName.equals(currentThreadName)) {
208                return threadId;
209            }
210        }
211        throw new TestErrorException("Could not find thread id of thread \"" + threadName + "\"");
212    }
213
214    /**
215     * Checks the stack frame contains the values we expect during the test.
216     *
217     * @param classID
218     *          the debuggee class ID
219     * @param eventThreadID
220     *          the thread ID of the event thread
221     * @param tester
222     *          an instance holding test logic
223     * @param suspensionMethodInfo
224     *          an instance providing the local variables (and their value) for the method
225     *          used for suspension. This is a special method because we do not update variables,
226     *          we only suspend the current thread.
227     * @param firstSuspension
228     *          true if the execution is suspended by the first breakpoint, false otherwise.
229     */
230    private void checkStackFrame(long classID, long eventThreadID, StackFrameTester tester,
231                                 MethodInfo suspensionMethodInfo, boolean firstSuspension) {
232        for (MethodInfo methodInfo : tester.getTestedMethods()) {
233            String testMethodName = methodInfo.getMethodName();
234            long testMethodID = getMethodID(classID, testMethodName);
235            assertTrue("No method " + testMethodName, testMethodID != -1);
236            boolean isSuspensionMethod = (methodInfo == suspensionMethodInfo);
237
238            VarInfo[] variables = jdwpGetVariableTable(classID, testMethodID);
239            assertNotNull("No variable table for method " + testMethodName, variables);
240
241            // Find the frame for the tested method.
242            FrameInfo testMethodFrame = getFrameInfo(eventThreadID, classID, testMethodID);
243            assertNotNull("Cannot find frame for method " + testMethodName, testMethodFrame);
244            logWriter.println("Found frame for " + testMethodName + ": " + testMethodFrame.frameID);
245
246            // Test all variables.
247            List<? extends VariableInfo> testedVariables = methodInfo.getVariables();
248            assertTrue("Not enough variables in variable table",
249                    variables.length >= testedVariables.size());
250            for (VariableInfo variableInfo : testedVariables) {
251                String variableName = variableInfo.getVariableName();
252
253                VarInfo testVarInfo = getVariableInfo(variables, variableName);
254                assertNotNull("No variable info for \"" + variableName + "\"", testVarInfo);
255
256                logWriter.println("Checking value for variable \"" + variableName + "\"");
257
258                // Check the current variable value is correct.
259                Value expectedValue;
260                if (firstSuspension) {
261                    expectedValue = variableInfo.getValueOnFirstSuspension();
262                } else {
263                    expectedValue = variableInfo.getValueOnSecondSuspension();
264                }
265                logWriter.println("Check variable \"" + variableName + "\" contains value \"" + expectedValue + "\"");
266                Value actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
267                        testVarInfo.getSlot(), expectedValue.getTag());
268                assertNotNull("No value for variable \"" + variableName + "\"", actual);
269                assertEquals("Incorrect value variable \"" + variableName + "\"",
270                        expectedValue, actual);
271
272                Value newValue = variableInfo.getValueToSet();
273                if (firstSuspension && newValue != null && !isSuspensionMethod) {
274                    // Sets the new value in the tested variable.
275                    setVariableValue(eventThreadID, testMethodFrame.frameID,
276                                     testVarInfo.getSlot(), newValue);
277
278                    // Checks the variable has been properly set.
279                    actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
280                            testVarInfo.getSlot(), newValue.getTag());
281                    assertNotNull("No value for variable \"" + variableName + "\"", actual);
282                    assertEquals("Failed to set variable \"" + variableName + "\"",
283                            newValue, actual);
284                }
285            }
286        }
287    }
288
289    /**
290     * Base class for checking stack frame operations.
291     */
292    static abstract class StackFrameChecker {
293        static class VariableNameAndTag {
294            public VariableNameAndTag(String testVariableName, byte testVariableJdwpTag) {
295                this.testVariableName = testVariableName;
296                this.testVariableJdwpTag = testVariableJdwpTag;
297            }
298
299            /**
300             * Returns the name of the variable (in the tested method) for which
301             * we want to check the value.
302             */
303            public String getTestVariableName() {
304                return testVariableName;
305            }
306
307            /**
308             * Returns the JDWP tag of the tested variable. This matches the
309             * declared type of the variable in the tested method.
310             *
311             * Note: it can be different from the value's tag we retrieve from
312             * the stack in the case of Object variable (like String).
313             */
314            public byte getTestVariableJdwpTag() {
315                return testVariableJdwpTag;
316            }
317
318            private final String testVariableName;
319            private final byte testVariableJdwpTag;
320
321        }
322
323        protected StackFrameChecker(String breakpointMethodName, String testMethodName,
324                String testVariableName, byte testVariableJdwpTag) {
325            this(breakpointMethodName, testMethodName);
326            addTestedVariable(testVariableName, testVariableJdwpTag);
327        }
328
329        protected StackFrameChecker(String breakpointMethodName, String testMethodName) {
330            this.breakpointMethodName = breakpointMethodName;
331            this.testMethodName = testMethodName;
332            this.testedVariables = new ArrayList<VariableNameAndTag>();
333        }
334
335        /**
336         * Returns the name of the method where a breakpoint is installed
337         * to suspend the debuggee.
338         */
339        public String getBreakpointMethodName() {
340            return breakpointMethodName;
341        }
342
343        /**
344         * Returns the name of the method calling the "breakpoint" method.
345         */
346        public String getTestMethodName() {
347            return testMethodName;
348        }
349
350        public int addTestedVariable(String name, byte tag) {
351            testedVariables.add(new VariableNameAndTag(name, tag));
352            return testedVariables.size() - 1;
353        }
354
355        public List<? extends VariableNameAndTag> getTestedVariables() {
356            return testedVariables;
357        }
358
359        protected String getTestVariableName(int idx) {
360            return getTestedVariables().get(idx).getTestVariableName();
361        }
362
363        private final String breakpointMethodName;
364        private final String testMethodName;
365        private final List<VariableNameAndTag> testedVariables;
366    }
367
368    /**
369     * Returns the {@link VarInfo} of the given variable in the given method.
370     *
371     * @param classID
372     *          the ID of the declaring class of the method
373     * @param methodID
374     *          the ID of the method
375     * @param variableName
376     *          the name of the variable we look for
377     */
378    protected VarInfo getVariableInfo(long classID, long methodID, String variableName) {
379        VarInfo[] variables = jdwpGetVariableTable(classID, methodID);
380        return getVariableInfo(variables, variableName);
381    }
382
383    protected VarInfo getVariableInfo(VarInfo[] variables, String variableName) {
384        for (VarInfo variable : variables) {
385            if (variable.name.equals(variableName)) {
386                return variable;
387            }
388        }
389        return null;
390    }
391
392    /**
393     * Returns the {@link FrameInfo} of the most recent frame matching the given method.
394     *
395     * @param threadID
396     *          the ID of the thread where to look for the frame
397     * @param classID
398     *          the ID of the declaring class of the method
399     * @param methodID
400     *          the ID of the method
401     */
402    protected FrameInfo getFrameInfo(long threadID, long classID, long methodID) {
403        int frameCount = jdwpGetFrameCount(threadID);
404
405        // There should be at least 2 frames: the breakpoint method and its caller.
406        assertTrue("Not enough frames", frameCount > 2);
407
408        FrameInfo[] frames = jdwpGetFrames(threadID, 0, frameCount);
409        for (FrameInfo frameInfo : frames) {
410            if (frameInfo.location.classID == classID &&
411                    frameInfo.location.methodID == methodID) {
412                return frameInfo;
413            }
414        }
415        return null;
416    }
417
418    /**
419     * Returns the value of a local variable in the stack.
420     *
421     * @param threadID
422     *          the ID of the thread of the stack
423     * @param frameID
424     *          the ID of the frame of the stack
425     * @param slot
426     *          the slot of the variable in the stack
427     * @param tag
428     *          the type of the value
429     */
430    protected Value getVariableValue(long threadID, long frameID, int slot, byte tag) {
431        logWriter.println(" Send StackFrame::GetValues: threadID=" + threadID +
432                          ", frameID=" + frameID + ", slot=" + slot +
433                          ", tag=" + JDWPConstants.Tag.getName(tag));
434
435        // Send StackFrame::GetValues command.
436        CommandPacket packet = new CommandPacket(
437                JDWPCommands.StackFrameCommandSet.CommandSetID,
438                JDWPCommands.StackFrameCommandSet.GetValuesCommand);
439        packet.setNextValueAsThreadID(threadID);
440        packet.setNextValueAsFrameID(frameID);
441        packet.setNextValueAsInt(1);
442        packet.setNextValueAsInt(slot);
443        packet.setNextValueAsByte(tag);
444
445        // Check reply has no error.
446        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
447        checkReplyPacket(reply, "StackFrame::GetValues command");
448
449        // Check we have 1 value.
450        int numberOfValues = reply.getNextValueAsInt();
451        assertEquals("Incorrect number of values", 1, numberOfValues);
452
453        // Check the value tag is correct.
454        Value value = reply.getNextValueAsValue();
455        logWriter.println(" Received value " + value);
456        assertEquals("Invalid value tag", tag, value.getTag());
457
458        assertAllDataRead(reply);
459        return value;
460    }
461
462    /**
463     * Sets the value of a local variable in the stack.
464     *
465     * @param threadID
466     *          the ID of the thread of the stack
467     * @param frameID
468     *          the ID of the frame of the stack
469     * @param slot
470     *          the slot of the variable in the stack
471     * @param newValue
472     *          the new value to set
473     */
474    protected void setVariableValue(long threadID, long frameID, int slot, Value newValue) {
475        logWriter.println(" Send StackFrame::SetValues: threadID=" + threadID +
476                          ", frameID=" + frameID + ", slot=" + slot +
477                          ", value=" + newValue);
478
479        // Send StackFrame::SetValues command.
480        CommandPacket packet = new CommandPacket(JDWPCommands.StackFrameCommandSet.CommandSetID,
481                                                 JDWPCommands.StackFrameCommandSet.SetValuesCommand);
482        packet.setNextValueAsThreadID(threadID);
483        packet.setNextValueAsFrameID(frameID);
484        packet.setNextValueAsInt(1);
485        packet.setNextValueAsInt(slot);
486        packet.setNextValueAsValue(newValue);
487
488        // Check reply has no error.
489        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
490        checkReplyPacket(reply, "StackFrame::SetValues command");
491    }
492
493    /**
494     * Returns the value of the static field identified by the given name in
495     * the given class.
496     *
497     * @param classID
498     *          the ID of the declaring class of the static field
499     * @param fieldName
500     *          the name of the static field in the class.
501     */
502    protected Value getStaticFieldValue(long classID, String fieldName) {
503        long fieldID = debuggeeWrapper.vmMirror.getFieldID(classID, fieldName);
504        long[] fieldIDs = new long[]{ fieldID };
505        Value[] fieldValues = debuggeeWrapper.vmMirror.getReferenceTypeValues(classID, fieldIDs);
506        assertNotNull("No field values", fieldValues);
507        assertEquals("Invalid field values count", fieldValues.length, 1);
508        return fieldValues[0];
509    }
510}
511