1 /*
2  * Copyright (c) 2001, 2018, 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  * @library /test/lib
35  *
36  * @run build TestScaffold VMConnection TargetListener TargetAdapter
37  * @run compile -g RedefineTest.java
38  * @run driver RedefineTest
39  */
40 import com.sun.jdi.*;
41 import com.sun.jdi.event.*;
42 import com.sun.jdi.request.*;
43 import jdk.test.lib.Utils;
44 import jdk.test.lib.compiler.InMemoryJavaCompiler;
45 
46 import java.nio.charset.StandardCharsets;
47 import java.nio.file.Files;
48 import java.nio.file.Path;
49 import java.nio.file.Paths;
50 import java.util.*;
51 import java.io.*;
52 
53     /********** target program **********/
54 
55 class RedefineTarg {
show(String where)56     public static void show(String where){
57         System.out.println("Returned: " + where);
58     }
59 
lastly(String where)60     public static void lastly(String where){
61     }
62 
main(String[] args)63     public static void main(String[] args){
64         RedefineSubTarg sub = new RedefineSubTarg();
65         String where = "";
66         for (int i = 0; i < 5; ++i) {
67             where = sub.foo(where);
68             show(where);
69         }
70         lastly(where);
71     }
72 }
73 
74     /********** test program **********/
75 
76 public class RedefineTest extends TestScaffold {
77     ReferenceType targetClass;
78     static final String expected ="Boring Boring Different Boring Different ";
79     int repetitionCount = 0;
80     boolean beforeRedefine = true;
81 
RedefineTest(String args[])82     RedefineTest (String args[]) {
83         super(args);
84     }
85 
main(String[] args)86     public static void main(String[] args)      throws Exception {
87         new RedefineTest(args).startTests();
88     }
89 
90     /********** event handlers **********/
91 
methodEntered(MethodEntryEvent event)92     public void methodEntered(MethodEntryEvent event) {
93         Method meth = event.location().method();
94         ThreadReference thread = event.thread();
95 
96         if (meth.name().equals("foo")) {
97             ++repetitionCount;
98             beforeRedefine = true;
99             try {
100                 expectNonObsolete(thread);
101                 inspectLineNumber(event, thread.frame(0));
102 
103                 doRedefine(thread);
104                 beforeRedefine = false;
105 
106                 switch (repetitionCount) {
107                 case 1:
108                 case 5:
109                     expectNonObsolete(thread);
110                     inspectLineNumber(event, thread.frame(0));
111                     break;
112                 case 2:
113                 case 3:
114                 case 4:
115                     expectObsolete(thread);
116                     inspectLineNumber(event, thread.frame(0));
117                     break;
118                 }
119 
120 
121             } catch (Exception exc) {
122                 failure("Test Failure: unexpected exception - " + exc);
123                 exc.printStackTrace();
124             }
125         }
126     }
127 
breakpointReached(BreakpointEvent event)128     public void breakpointReached(BreakpointEvent event) {
129         ThreadReference thread = event.thread();
130         try {
131             StackFrame frame = thread.frame(0);
132             LocalVariable lv = frame.visibleVariableByName("where");
133             Value vWhere = frame.getValue(lv);
134             String remoteWhere = ((StringReference)vWhere).value();
135             println("Value of where: " + remoteWhere);
136             if (!remoteWhere.equals(expected)) {
137                 failure("FAIL: expected result string: '" + expected +
138                         "' got: '" + remoteWhere + "'");
139             }
140         } catch (Exception thr) {
141             failure("Test Failure: unexpected exception: " + thr);
142         }
143     }
144 
145     /********** test assists **********/
146 
expectNonObsolete(ThreadReference thread)147     void expectNonObsolete(ThreadReference thread) throws Exception {
148         if (isObsolete(thread)) {
149             failure("FAIL: Method should NOT be obsolete");
150         } else {
151             println("as it should be, not obsolete");
152         }
153     }
154 
expectObsolete(ThreadReference thread)155     void expectObsolete(ThreadReference thread) throws Exception {
156         if (isObsolete(thread)) {
157             println("obsolete like it should be");
158         } else {
159             failure("FAIL: Method should be obsolete");
160         }
161     }
162 
inspectLineNumber(LocatableEvent event, StackFrame frame)163     void inspectLineNumber(LocatableEvent event, StackFrame frame) throws Exception {
164         /*
165          * For each value of repetitionCount, use the beforeRedefine
166          * boolean to distinguish the time before and after the actual
167          * redefinition takes place.  Line numbers are inspected both
168          * before and after each redefine.
169          */
170         int n = -1;
171         int expectedLine = -1;
172         switch (repetitionCount) {
173         case 1:
174             expectedLine = 4;
175             break;
176         case 2:
177             expectedLine = beforeRedefine ? 4:21;
178             break;
179         case 3:
180             expectedLine = beforeRedefine ? 21:4;
181             break;
182         case 4:
183             expectedLine = beforeRedefine ? 4:21;
184             break;
185         case 5:
186             /* The class won't be redefined on this iteration (look
187              * for a java.lang.UnsupportedOperationException instead)
188              * so expected line stays the same as last successful
189              * redefine.
190              */
191             expectedLine = 21;
192             break;
193         }
194         Method method = event.location().method();
195         if (frame.location().method().isObsolete()) {
196             /*
197              * Then skip. Obsolete methods are not interesting to
198              * inspect.
199              */
200             println("inspectLineNumber skipping obsolete method " + method.name());
201         } else {
202             n = method.location().lineNumber();
203             int m = frame.location().lineNumber();
204             if ((n != expectedLine) || (n != m)) {
205                 failure("Test Failure: line number disagreement: " +
206                         n + " (event) versus " + m + " (frame) versus " + expectedLine +
207                         " (expected)");
208             } else {
209                 println("inspectLineNumber in method " + method.name() + " at line " + n);
210             }
211         }
212     }
213 
isObsolete(ThreadReference thread)214     boolean isObsolete(ThreadReference thread) throws Exception {
215         StackFrame frame = thread.frame(0);
216         Method meth = frame.location().method();
217         return meth.isObsolete();
218     }
219 
doRedefine(ThreadReference thread)220     void doRedefine(ThreadReference thread) throws Exception {
221         Exception receivedException = null;
222         String fileName = "notThis";
223 
224         switch (repetitionCount) {
225         case 1:
226             fileName = "RedefineSubTarg.class";
227             break;
228         case 2:
229             fileName = "Different_RedefineSubTarg.class";
230             break;
231         case 3:
232             fileName = "RedefineSubTarg.class";
233             break;
234         case 4:
235             fileName = "Different_RedefineSubTarg.class";
236             break;
237         case 5:
238             fileName = "SchemaChange_RedefineSubTarg.class";
239             break;
240         }
241         File phyl = new File(fileName);
242         byte[] bytes = new byte[(int)phyl.length()];
243         InputStream in = new FileInputStream(phyl);
244         in.read(bytes);
245         in.close();
246 
247         Map map = new HashMap();
248         map.put(findReferenceType("RedefineSubTarg"), bytes);
249 
250         println(System.getProperty("line.separator") + "Iteration # " + repetitionCount +
251                 " ------ Redefine as: " + fileName);
252         try {
253             vm().redefineClasses(map);
254         } catch (Exception thr) {
255             receivedException = thr;
256         }
257         switch (repetitionCount) {
258         case 5:
259             if (receivedException == null) {
260                 failure("FAIL: no exception; expected: UnsupportedOperationException");
261             } else if (receivedException instanceof UnsupportedOperationException) {
262                 println("Received expected exception: " + receivedException);
263             } else {
264                 failure("FAIL: got exception: " + receivedException +
265                         ", expected: UnsupportedOperationException");
266             }
267             break;
268         default:
269             if (receivedException != null) {
270                 failure("FAIL: unexpected exception: " +
271                         receivedException);
272             }
273             break;
274         }
275         return;
276     }
277 
278     // prepares .class file for redefined RedefineSubTarg class:
279     // - compiles <fileName>.java from test source dir;
280     // - saves compiled class <fileName>.class.
prepareRedefinedClass(String fileName)281     protected void prepareRedefinedClass(String fileName) throws Exception {
282         Path srcJavaFile = Paths.get(Utils.TEST_SRC).resolve(fileName + ".java");
283         Path dstClassFile = Paths.get(".").resolve(fileName + ".class");
284         byte[] compiledData = InMemoryJavaCompiler.compile("RedefineSubTarg",
285                 new String(Files.readAllBytes(srcJavaFile), StandardCharsets.UTF_8),
286                 "-g");
287         Files.write(dstClassFile, compiledData);
288     }
289 
290     /********** test core **********/
291 
runTests()292     protected void runTests() throws Exception {
293         // prepare redefined .class files
294         prepareRedefinedClass("Different_RedefineSubTarg");
295         prepareRedefinedClass("SchemaChange_RedefineSubTarg");
296         prepareRedefinedClass("RedefineSubTarg");
297 
298         BreakpointEvent bpe = startToMain("RedefineTarg");
299         targetClass = bpe.location().declaringType();
300         EventRequestManager erm = vm().eventRequestManager();
301 
302         /*
303          * Method entry in sub targ
304          */
305         MethodEntryRequest mee = erm.createMethodEntryRequest();
306         mee.addClassFilter("RedefineSubTarg");
307         mee.enable();
308 
309         /*
310          * BP at end to get value
311          */
312         List lastlys = targetClass.methodsByName("lastly");
313         if (lastlys.size() != 1) {
314             throw new Exception ("TestFailure: Expected one 'lastly' method, found: " +
315                                  lastlys);
316         }
317         Location loc = ((Method)(lastlys.get(0))).location();
318         EventRequest req = erm.createBreakpointRequest(loc);
319         req.enable();
320 
321         // Allow application to complete and shut down
322         listenUntilVMDisconnect();
323 
324         /*
325          * deal with results of test
326          * if anything has called failure("foo") testFailed will be true
327          */
328         if (!testFailed) {
329             println("RedefineTest: passed");
330         } else {
331             throw new Exception("RedefineTest: failed");
332         }
333     }
334 }
335