1/*
2 * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @bug 4287595
27 * @bug 4462989
28 * @bug 4531511
29 * @summary Test class redefinition
30 *
31 * @author Robert Field
32 *
33 * @library ..
34 *
35 * @run build TestScaffold VMConnection TargetListener TargetAdapter
36 * @run compile -g RedefineTest.java
37 * @run shell RedefineSetUp.sh
38 * @run driver RedefineTest
39 */
40import com.sun.jdi.*;
41import com.sun.jdi.event.*;
42import com.sun.jdi.request.*;
43import java.util.*;
44import java.io.*;
45
46    /********** target program **********/
47
48class RedefineTarg {
49    public static void show(String where){
50        System.out.println("Returned: " + where);
51    }
52
53    public static void lastly(String where){
54    }
55
56    public static void main(String[] args){
57        RedefineSubTarg sub = new RedefineSubTarg();
58        String where = "";
59        for (int i = 0; i < 5; ++i) {
60            where = sub.foo(where);
61            show(where);
62        }
63        lastly(where);
64    }
65}
66
67    /********** test program **********/
68
69public class RedefineTest extends TestScaffold {
70    ReferenceType targetClass;
71    static final String expected ="Boring Boring Different Boring Different ";
72    int repetitionCount = 0;
73    boolean beforeRedefine = true;
74
75    RedefineTest (String args[]) {
76        super(args);
77    }
78
79    public static void main(String[] args)      throws Exception {
80        new RedefineTest(args).startTests();
81    }
82
83    /********** event handlers **********/
84
85    public void methodEntered(MethodEntryEvent event) {
86        Method meth = event.location().method();
87        ThreadReference thread = event.thread();
88
89        if (meth.name().equals("foo")) {
90            ++repetitionCount;
91            beforeRedefine = true;
92            try {
93                expectNonObsolete(thread);
94                inspectLineNumber(event, thread.frame(0));
95
96                doRedefine(thread);
97                beforeRedefine = false;
98
99                switch (repetitionCount) {
100                case 1:
101                case 5:
102                    expectNonObsolete(thread);
103                    inspectLineNumber(event, thread.frame(0));
104                    break;
105                case 2:
106                case 3:
107                case 4:
108                    expectObsolete(thread);
109                    inspectLineNumber(event, thread.frame(0));
110                    break;
111                }
112
113
114            } catch (Exception exc) {
115                failure("Test Failure: unexpected exception - " + exc);
116                exc.printStackTrace();
117            }
118        }
119    }
120
121    public void breakpointReached(BreakpointEvent event) {
122        ThreadReference thread = event.thread();
123        try {
124            StackFrame frame = thread.frame(0);
125            LocalVariable lv = frame.visibleVariableByName("where");
126            Value vWhere = frame.getValue(lv);
127            String remoteWhere = ((StringReference)vWhere).value();
128            println("Value of where: " + remoteWhere);
129            if (!remoteWhere.equals(expected)) {
130                failure("FAIL: expected result string: '" + expected +
131                        "' got: '" + remoteWhere + "'");
132            }
133        } catch (Exception thr) {
134            failure("Test Failure: unexpected exception: " + thr);
135        }
136    }
137
138    /********** test assists **********/
139
140    void expectNonObsolete(ThreadReference thread) throws Exception {
141        if (isObsolete(thread)) {
142            failure("FAIL: Method should NOT be obsolete");
143        } else {
144            println("as it should be, not obsolete");
145        }
146    }
147
148    void expectObsolete(ThreadReference thread) throws Exception {
149        if (isObsolete(thread)) {
150            println("obsolete like it should be");
151        } else {
152            failure("FAIL: Method should be obsolete");
153        }
154    }
155
156    void inspectLineNumber(LocatableEvent event, StackFrame frame) throws Exception {
157        /*
158         * For each value of repetitionCount, use the beforeRedefine
159         * boolean to distinguish the time before and after the actual
160         * redefinition takes place.  Line numbers are inspected both
161         * before and after each redefine.
162         */
163        int n = -1;
164        int expectedLine = -1;
165        switch (repetitionCount) {
166        case 1:
167            expectedLine = 4;
168            break;
169        case 2:
170            expectedLine = beforeRedefine ? 4:21;
171            break;
172        case 3:
173            expectedLine = beforeRedefine ? 21:4;
174            break;
175        case 4:
176            expectedLine = beforeRedefine ? 4:21;
177            break;
178        case 5:
179            /* The class won't be redefined on this iteration (look
180             * for a java.lang.UnsupportedOperationException instead)
181             * so expected line stays the same as last successful
182             * redefine.
183             */
184            expectedLine = 21;
185            break;
186        }
187        Method method = event.location().method();
188        if (frame.location().method().isObsolete()) {
189            /*
190             * Then skip. Obsolete methods are not interesting to
191             * inspect.
192             */
193            println("inspectLineNumber skipping obsolete method " + method.name());
194        } else {
195            n = method.location().lineNumber();
196            int m = frame.location().lineNumber();
197            if ((n != expectedLine) || (n != m)) {
198                failure("Test Failure: line number disagreement: " +
199                        n + " (event) versus " + m + " (frame) versus " + expectedLine +
200                        " (expected)");
201            } else {
202                println("inspectLineNumber in method " + method.name() + " at line " + n);
203            }
204        }
205    }
206
207    boolean isObsolete(ThreadReference thread) throws Exception {
208        StackFrame frame = thread.frame(0);
209        Method meth = frame.location().method();
210        return meth.isObsolete();
211    }
212
213    void doRedefine(ThreadReference thread) throws Exception {
214        Exception receivedException = null;
215        String fileName = "notThis";
216
217        switch (repetitionCount) {
218        case 1:
219            fileName = "RedefineSubTarg.class";
220            break;
221        case 2:
222            fileName = "Different_RedefineSubTarg.class";
223            break;
224        case 3:
225            fileName = "RedefineSubTarg.class";
226            break;
227        case 4:
228            fileName = "Different_RedefineSubTarg.class";
229            break;
230        case 5:
231            fileName = "SchemaChange_RedefineSubTarg.class";
232            break;
233        }
234        File phyl = new File(fileName);
235        byte[] bytes = new byte[(int)phyl.length()];
236        InputStream in = new FileInputStream(phyl);
237        in.read(bytes);
238        in.close();
239
240        Map map = new HashMap();
241        map.put(findReferenceType("RedefineSubTarg"), bytes);
242
243        println(System.getProperty("line.separator") + "Iteration # " + repetitionCount +
244                " ------ Redefine as: " + fileName);
245        try {
246            vm().redefineClasses(map);
247        } catch (Exception thr) {
248            receivedException = thr;
249        }
250        switch (repetitionCount) {
251        case 5:
252            if (receivedException == null) {
253                failure("FAIL: no exception; expected: UnsupportedOperationException");
254            } else if (receivedException instanceof UnsupportedOperationException) {
255                println("Received expected exception: " + receivedException);
256            } else {
257                failure("FAIL: got exception: " + receivedException +
258                        ", expected: UnsupportedOperationException");
259            }
260            break;
261        default:
262            if (receivedException != null) {
263                failure("FAIL: unexpected exception: " +
264                        receivedException);
265            }
266            break;
267        }
268        return;
269    }
270
271    /********** test core **********/
272
273    protected void runTests() throws Exception {
274
275        BreakpointEvent bpe = startToMain("RedefineTarg");
276        targetClass = bpe.location().declaringType();
277        EventRequestManager erm = vm().eventRequestManager();
278
279        /*
280         * Method entry in sub targ
281         */
282        MethodEntryRequest mee = erm.createMethodEntryRequest();
283        mee.addClassFilter("RedefineSubTarg");
284        mee.enable();
285
286        /*
287         * BP at end to get value
288         */
289        List lastlys = targetClass.methodsByName("lastly");
290        if (lastlys.size() != 1) {
291            throw new Exception ("TestFailure: Expected one 'lastly' method, found: " +
292                                 lastlys);
293        }
294        Location loc = ((Method)(lastlys.get(0))).location();
295        EventRequest req = erm.createBreakpointRequest(loc);
296        req.enable();
297
298        // Allow application to complete and shut down
299        listenUntilVMDisconnect();
300
301        /*
302         * deal with results of test
303         * if anything has called failure("foo") testFailed will be true
304         */
305        if (!testFailed) {
306            println("RedefineTest: passed");
307        } else {
308            throw new Exception("RedefineTest: failed");
309        }
310    }
311}
312