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.jdwp.CommandPacket;
22import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
23import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
24import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
25import org.apache.harmony.jpda.tests.framework.jdwp.Value;
26import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
27
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * Base class for testing StackFrame.GetValues and StackFrame.SetValues commands.
33 */
34public class JDWPStackFrameAccessTest extends JDWPStackFrameTestCase {
35    @Override
36    protected final String getDebuggeeClassName() {
37        return StackTrace002Debuggee.class.getName();
38    }
39
40    static class VariableInfo {
41        private final String variableName;
42        private final Value initialValue;
43        private final Value newValue;
44
45        VariableInfo(String variableName, Value initialValue, Value newValue) {
46            this.variableName = variableName;
47            this.initialValue = initialValue;
48            this.newValue = newValue;
49        }
50
51        public String getVariableName() {
52            return variableName;
53        }
54
55        public Value getInitialValue() {
56            return initialValue;
57        }
58
59        public Value getNewValue() {
60            return newValue;
61        }
62    }
63
64    static class MethodInfo {
65        private final String methodName;
66        private final List<VariableInfo> variables = new ArrayList<VariableInfo>();
67
68        MethodInfo(String methodName) {
69            this.methodName = methodName;
70        }
71
72        public String getMethodName() {
73            return methodName;
74        }
75
76        public List<? extends VariableInfo> getVariables() {
77            return variables;
78        }
79
80        public void addVariable(String variableName, Value initialValue, Value newValue) {
81            variables.add(new VariableInfo(variableName, initialValue, newValue));
82        }
83
84        public void addVariable(String variableName, Value initialValue) {
85            addVariable(variableName, initialValue, null);
86        }
87    }
88
89    static class StackFrameTester {
90        // TODO remove when we no longer need breakpoint to suspend.
91        private final String breakpointMethodName;
92        private final String signalValue;
93        private final List<MethodInfo> testedMethods = new ArrayList<MethodInfo>();
94
95        public StackFrameTester(String breakpointMethodName, String signalValue) {
96            this.breakpointMethodName = breakpointMethodName;
97            this.signalValue = signalValue;
98        }
99
100        public String getBreakpointMethodName() {
101            return breakpointMethodName;
102        }
103
104        public String getSignalValue() {
105            return signalValue;
106        }
107
108        public List<? extends MethodInfo> getTestedMethods() {
109            return testedMethods;
110        }
111
112        public MethodInfo addTestMethod(String methodName) {
113            MethodInfo methodInfo = new MethodInfo(methodName);
114            testedMethods.add(methodInfo);
115            return methodInfo;
116        }
117    }
118
119    @Override
120    protected void internalSetUp() throws Exception {
121        super.internalSetUp();
122
123        printTestLog("STARTED");
124
125        // Wait for debuggee to start.
126        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
127    }
128
129    @Override
130    protected void internalTearDown() {
131        // Resume debuggee.
132        debuggeeWrapper.vmMirror.resume();
133
134        printTestLog("FINISHED");
135
136        super.internalTearDown();
137    }
138
139    protected void runStackFrameTest(StackFrameTester tester) {
140        // Get variable information.
141        long classID = getClassIDBySignature(getDebuggeeClassSignature());
142
143        // Install breakpoint.
144        int breakpointRequestID = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
145                tester.getBreakpointMethodName());
146
147        // Signal debuggee with a custom message to execute the right method.
148        synchronizer.sendMessage(tester.getSignalValue());
149
150        // Wait for 1st breakpoint hit.
151        long eventThreadID = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointRequestID);
152
153        // Check every local variable of every method.
154        checkStackFrame(classID, eventThreadID, tester, true);
155
156        // Resume debuggee.
157        debuggeeWrapper.vmMirror.resume();
158
159        // Wait for 2nd breakpoint hit.
160        eventThreadID = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointRequestID);
161
162        // Remove the breakpoint.
163        debuggeeWrapper.vmMirror.clearBreakpoint(breakpointRequestID);
164
165        // Check every local variable of every method.
166        checkStackFrame(classID, eventThreadID, tester, false);
167    }
168
169    /**
170     * Checks the stack frame contains the values we expect during the test.
171     *
172     * @param classID
173     *          the debuggee class ID
174     * @param eventThreadID
175     *          the thread ID of the event thread
176     * @param tester
177     *          an instance holding test logic
178     * @param firstSuspension
179     *          true if the execution is suspended by the first breakpoint, false otherwise.
180     */
181    private void checkStackFrame(long classID, long eventThreadID, StackFrameTester tester,
182                                 boolean firstSuspension) {
183        for (MethodInfo methodInfo : tester.getTestedMethods()) {
184            String testMethodName = methodInfo.getMethodName();
185            long testMethodID = getMethodID(classID, testMethodName);
186
187            VarInfo[] variables = jdwpGetVariableTable(classID, testMethodID);
188            assertNotNull("No variable table for method " + testMethodName, variables);
189
190            // Find the frame for the tested method.
191            FrameInfo testMethodFrame = getFrameInfo(eventThreadID, classID, testMethodID);
192            assertNotNull("Cannot find frame for method " + testMethodName, testMethodFrame);
193            logWriter.println("Found frame for " + testMethodName + ": " + testMethodFrame.frameID);
194
195            // Test all variables.
196            List<? extends VariableInfo> testedVariables = methodInfo.getVariables();
197            assertTrue("Not enough variables in variable table",
198                    variables.length >= testedVariables.size());
199            for (VariableInfo variableInfo : testedVariables) {
200                String variableName = variableInfo.getVariableName();
201
202                VarInfo testVarInfo = getVariableInfo(variables, variableName);
203                assertNotNull("No variable info for \"" + variableName + "\"", testVarInfo);
204
205                logWriter.println("Checking value for variable \"" + variableName + "\"");
206
207                Value initialValue = variableInfo.getInitialValue();
208                Value newValue = variableInfo.getNewValue();
209
210                // TODO specialize between GetValues and SetValues tests.
211                if (firstSuspension) {
212                    if (newValue != null) {
213                        // Sets the new value in the tested variable.
214                        setVariableValue(eventThreadID, testMethodFrame.frameID,
215                                         testVarInfo.getSlot(), newValue);
216                    } else {
217                        // Check the value is the expected one.
218                        Value expected = initialValue;
219                        Value actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
220                                testVarInfo.getSlot(), expected.getTag());
221                        assertNotNull("No value for variable \"" + variableName + "\"", actual);
222                        assertEquals("Incorrect value variable \"" + variableName + "\"",
223                                expected, actual);
224                    }
225                } else {
226                    if (newValue != null) {
227                        // Check the value is the expected one.
228                        Value expected = newValue;
229                        Value actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
230                                testVarInfo.getSlot(), expected.getTag());
231                        assertNotNull("No value for variable \"" + variableName + "\"", actual);
232                        assertEquals("Incorrect value variable \"" + variableName + "\"",
233                                expected, actual);
234                    } else {
235                        // Nothing to do.
236                    }
237                }
238            }
239        }
240    }
241
242    /**
243     * Base class for checking stack frame operations.
244     */
245    static abstract class StackFrameChecker {
246        static class VariableNameAndTag {
247            public VariableNameAndTag(String testVariableName, byte testVariableJdwpTag) {
248                this.testVariableName = testVariableName;
249                this.testVariableJdwpTag = testVariableJdwpTag;
250            }
251
252            /**
253             * Returns the name of the variable (in the tested method) for which
254             * we want to check the value.
255             */
256            public String getTestVariableName() {
257                return testVariableName;
258            }
259
260            /**
261             * Returns the JDWP tag of the tested variable. This matches the
262             * declared type of the variable in the tested method.
263             *
264             * Note: it can be different from the value's tag we retrieve from
265             * the stack in the case of Object variable (like String).
266             */
267            public byte getTestVariableJdwpTag() {
268                return testVariableJdwpTag;
269            }
270
271            private final String testVariableName;
272            private final byte testVariableJdwpTag;
273
274        }
275
276        protected StackFrameChecker(String breakpointMethodName, String testMethodName,
277                String testVariableName, byte testVariableJdwpTag) {
278            this(breakpointMethodName, testMethodName);
279            addTestedVariable(testVariableName, testVariableJdwpTag);
280        }
281
282        protected StackFrameChecker(String breakpointMethodName, String testMethodName) {
283            this.breakpointMethodName = breakpointMethodName;
284            this.testMethodName = testMethodName;
285            this.testedVariables = new ArrayList<VariableNameAndTag>();
286        }
287
288        /**
289         * Returns the name of the method where a breakpoint is installed
290         * to suspend the debuggee.
291         */
292        public String getBreakpointMethodName() {
293            return breakpointMethodName;
294        }
295
296        /**
297         * Returns the name of the method calling the "breakpoint" method.
298         */
299        public String getTestMethodName() {
300            return testMethodName;
301        }
302
303        public int addTestedVariable(String name, byte tag) {
304            testedVariables.add(new VariableNameAndTag(name, tag));
305            return testedVariables.size() - 1;
306        }
307
308        public List<? extends VariableNameAndTag> getTestedVariables() {
309            return testedVariables;
310        }
311
312        protected String getTestVariableName(int idx) {
313            return getTestedVariables().get(idx).getTestVariableName();
314        }
315
316        private final String breakpointMethodName;
317        private final String testMethodName;
318        private final List<VariableNameAndTag> testedVariables;
319    }
320
321    /**
322     * Returns the {@link VarInfo} of the given variable in the given method.
323     *
324     * @param classID
325     *          the ID of the declaring class of the method
326     * @param methodID
327     *          the ID of the method
328     * @param variableName
329     *          the name of the variable we look for
330     */
331    protected VarInfo getVariableInfo(long classID, long methodID, String variableName) {
332        VarInfo[] variables = jdwpGetVariableTable(classID, methodID);
333        return getVariableInfo(variables, variableName);
334    }
335
336    protected VarInfo getVariableInfo(VarInfo[] variables, String variableName) {
337        for (VarInfo variable : variables) {
338            if (variable.name.equals(variableName)) {
339                return variable;
340            }
341        }
342        return null;
343    }
344
345    /**
346     * Returns the {@link FrameInfo} of the most recent frame matching the given method.
347     *
348     * @param threadID
349     *          the ID of the thread where to look for the frame
350     * @param classID
351     *          the ID of the declaring class of the method
352     * @param methodID
353     *          the ID of the method
354     */
355    protected FrameInfo getFrameInfo(long threadID, long classID, long methodID) {
356        int frameCount = jdwpGetFrameCount(threadID);
357
358        // There should be at least 2 frames: the breakpoint method and its caller.
359        assertTrue("Not enough frames", frameCount > 2);
360
361        FrameInfo[] frames = jdwpGetFrames(threadID, 0, frameCount);
362        for (FrameInfo frameInfo : frames) {
363            if (frameInfo.location.classID == classID &&
364                    frameInfo.location.methodID == methodID) {
365                return frameInfo;
366            }
367        }
368        return null;
369    }
370
371    /**
372     * Returns the value of a local variable in the stack.
373     *
374     * @param threadID
375     *          the ID of the thread of the stack
376     * @param frameID
377     *          the ID of the frame of the stack
378     * @param slot
379     *          the slot of the variable in the stack
380     * @param tag
381     *          the type of the value
382     */
383    protected Value getVariableValue(long threadID, long frameID, int slot, byte tag) {
384        logWriter.println(" Send StackFrame::GetValues: threadID=" + threadID +
385                          ", frameID=" + frameID + ", slot=" + slot +
386                          ", tag=" + JDWPConstants.Tag.getName(tag));
387
388        // Send StackFrame::GetValues command.
389        CommandPacket packet = new CommandPacket(
390                JDWPCommands.StackFrameCommandSet.CommandSetID,
391                JDWPCommands.StackFrameCommandSet.GetValuesCommand);
392        packet.setNextValueAsThreadID(threadID);
393        packet.setNextValueAsFrameID(frameID);
394        packet.setNextValueAsInt(1);
395        packet.setNextValueAsInt(slot);
396        packet.setNextValueAsByte(tag);
397
398        // Check reply has no error.
399        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
400        checkReplyPacket(reply, "StackFrame::GetValues command");
401
402        // Check we have 1 value.
403        int numberOfValues = reply.getNextValueAsInt();
404        assertEquals("Incorrect number of values", 1, numberOfValues);
405
406        // Check the value tag is correct.
407        Value value = reply.getNextValueAsValue();
408        logWriter.println(" Received value " + value);
409        assertEquals("Invalid value tag", tag, value.getTag());
410
411        assertAllDataRead(reply);
412        return value;
413    }
414
415    /**
416     * Sets the value of a local variable in the stack.
417     *
418     * @param threadID
419     *          the ID of the thread of the stack
420     * @param frameID
421     *          the ID of the frame of the stack
422     * @param slot
423     *          the slot of the variable in the stack
424     * @param newValue
425     *          the new value to set
426     */
427    protected void setVariableValue(long threadID, long frameID, int slot, Value newValue) {
428        logWriter.println(" Send StackFrame::SetValues: threadID=" + threadID +
429                          ", frameID=" + frameID + ", slot=" + slot +
430                          ", value=" + newValue);
431
432        // Send StackFrame::SetValues command.
433        CommandPacket packet = new CommandPacket(JDWPCommands.StackFrameCommandSet.CommandSetID,
434                                                 JDWPCommands.StackFrameCommandSet.SetValuesCommand);
435        packet.setNextValueAsThreadID(threadID);
436        packet.setNextValueAsFrameID(frameID);
437        packet.setNextValueAsInt(1);
438        packet.setNextValueAsInt(slot);
439        packet.setNextValueAsValue(newValue);
440
441        // Check reply has no error.
442        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
443        checkReplyPacket(reply, "StackFrame::SetValues command");
444    }
445
446    /**
447     * Returns the value of the static field identified by the given name in
448     * the given class.
449     *
450     * @param classID
451     *          the ID of the declaring class of the static field
452     * @param fieldName
453     *          the name of the static field in the class.
454     */
455    protected Value getStaticFieldValue(long classID, String fieldName) {
456        long fieldID = debuggeeWrapper.vmMirror.getFieldID(classID, fieldName);
457        long[] fieldIDs = new long[]{ fieldID };
458        Value[] fieldValues = debuggeeWrapper.vmMirror.getReferenceTypeValues(classID, fieldIDs);
459        assertNotNull("No field values", fieldValues);
460        assertEquals("Invalid field values count", fieldValues.length, 1);
461        return fieldValues[0];
462    }
463}
464