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 19 /** 20 * @author Anatoly F. Bondarenko 21 */ 22 23 /** 24 * Created on 30.03.2005 25 */ 26 package org.apache.harmony.jpda.tests.jdwp.VirtualMachine; 27 28 29 30 import java.io.*; 31 import java.util.Base64; 32 33 import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; 34 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands; 35 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; 36 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; 37 import org.apache.harmony.jpda.tests.jdwp.share.JDWPSyncTestCase; 38 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer; 39 40 /** 41 * JDWP Unit test for VirtualMachine.RedefineClasses command. 42 */ 43 public class RedefineClassesTest extends JDWPSyncTestCase { 44 45 static final int testStatusPassed = 0; 46 static final int testStatusFailed = -1; 47 static final String thisCommandName = "VirtualMachine::RedefineClasses command"; 48 static final String checkedClassSignature = 49 getClassSignature(RedefineClassesDebuggee.class).replace("RedefineClassesDebuggee", 50 "RedefineClass_Debuggee"); 51 static final String byteCodeToRedefineFile = "RedefineByteCode_Debuggee001"; 52 private static String thisTestName; 53 54 @Override getDebuggeeClassName()55 protected String getDebuggeeClassName() { 56 return RedefineClassesDebuggee.class.getName(); 57 } 58 findNewClassByteCode()59 File findNewClassByteCode() { 60 File foundFile = null; 61 String nameSeparator = File.separator; 62 String pathSeparator = File.pathSeparator; 63 String byteCodeFileNameSuffix = "org" + nameSeparator + "apache" + nameSeparator 64 + "harmony" + nameSeparator + "jpda" + nameSeparator + "tests" + nameSeparator 65 + "jdwp" + nameSeparator + "VirtualMachine" 66 + nameSeparator + byteCodeToRedefineFile; 67 String byteCodeFileName = null; 68 String classPaths = System.getProperty("java.class.path"); 69 int begPos = 0; 70 int classPathsLength = classPaths.length(); 71 72 for (int i = 0; i <= classPathsLength; i++) { 73 if ( i == classPathsLength ) { 74 if ( begPos == i ) { 75 break; 76 } 77 } else { 78 if ( ! pathSeparator.equals(classPaths.substring(i,i+1))) { 79 continue; 80 } 81 if ( begPos == i ) { 82 begPos++; 83 continue; 84 } 85 } 86 byteCodeFileName = classPaths.substring(begPos,i); 87 if ( ! nameSeparator.equals(classPaths.substring(i-1,i)) ) { 88 byteCodeFileName = byteCodeFileName + nameSeparator; 89 } 90 byteCodeFileName = byteCodeFileName + byteCodeFileNameSuffix; 91 foundFile = new File(byteCodeFileName); 92 if ( foundFile.exists() ) { 93 break; 94 } 95 foundFile = null; 96 begPos = i+1; 97 } 98 return foundFile; 99 } 100 getNewClassBytesClass()101 private byte[] getNewClassBytesClass() { 102 File newClassByteCodeFile = findNewClassByteCode(); 103 if ( newClassByteCodeFile == null ) { 104 logWriter.println 105 ("===> Can NOT find out byte code file for redefine:"); 106 logWriter.println 107 ("===> File name = " + byteCodeToRedefineFile); 108 logWriter.println 109 ("===> Test can NOT be run!"); 110 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 111 return null; 112 } 113 114 logWriter.println("=> File name with new class byte code to redefine = " + 115 byteCodeToRedefineFile); 116 FileInputStream newClassByteCodeFileInputStream = null; 117 try { 118 newClassByteCodeFileInputStream = new FileInputStream(newClassByteCodeFile); 119 } catch (Throwable thrown) { 120 logWriter.println("===> Can NOT create FileInputStream for byte code file:"); 121 logWriter.println("===> File name = " + byteCodeToRedefineFile); 122 logWriter.println("===> Exception is thrown: " + thrown); 123 logWriter.println("===> Test can NOT be run!"); 124 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 125 return null; 126 } 127 int newClassByteCodeSize = 0; 128 try { 129 newClassByteCodeSize = (int)newClassByteCodeFileInputStream.skip(Long.MAX_VALUE); 130 } catch (Throwable thrown) { 131 logWriter.println("===> Can NOT do FileInputStream.skip() to the end of file:"); 132 logWriter.println("===> File name = " + byteCodeToRedefineFile); 133 logWriter.println("===> Exception is thrown: " + thrown); 134 logWriter.println("===> Test can NOT be run!"); 135 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 136 return null; 137 } 138 logWriter.println("=> newClassByteCodeSize = " + newClassByteCodeSize); 139 try { 140 newClassByteCodeFileInputStream.close(); 141 } catch (Throwable thrown) { 142 logWriter.println 143 ("===> WARNING: Can NOT close FileInputStream for byte code file:"); 144 logWriter.println("===> File name = " + byteCodeToRedefineFile); 145 logWriter.println("===> Exception is thrown: " + thrown); 146 } 147 newClassByteCodeFileInputStream = null; 148 149 try { 150 newClassByteCodeFileInputStream = new FileInputStream(newClassByteCodeFile); 151 } catch (Throwable thrown) { 152 logWriter.println 153 ("===> Can NOT re-create FileInputStream for byte code file:"); 154 logWriter.println("===> File name = " + byteCodeToRedefineFile); 155 logWriter.println("===> Exception is thrown: " + thrown); 156 logWriter.println("===> Test can NOT be run!"); 157 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 158 return null; 159 } 160 byte[] res = new byte[newClassByteCodeSize]; 161 int totalRead = 0; 162 try { 163 totalRead = newClassByteCodeFileInputStream.read(res); 164 } catch (Throwable thrown) { 165 logWriter.println("===> Can NOT read current byte code file:"); 166 logWriter.println("===> File name = " + byteCodeToRedefineFile); 167 logWriter.println("===> Exception is thrown: " + thrown); 168 logWriter.println("===> Test can NOT be run!"); 169 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 170 return null; 171 } 172 if ( totalRead != newClassByteCodeSize) { // EOF is reached 173 logWriter.println("===> Could not read full bytecode file:"); 174 logWriter.println("===> File name = " + byteCodeToRedefineFile); 175 logWriter.println("===> expected to read: " + newClassByteCodeSize); 176 logWriter.println("===> actually read: " + totalRead); 177 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 178 return null; 179 } 180 return res; 181 } 182 getNewClassBytesDex()183 private byte[] getNewClassBytesDex() { 184 // This is a base64 dex-file for the following class 185 // package org.apache.harmony.jpda.tests.jdwp.VirtualMachine; 186 // class RedefineClass_Debuggee { 187 // static String testMethod() { 188 // return "testMethod_Result_After_Redefine"; 189 // } 190 // } 191 logWriter.println("===> Redefining class " + checkedClassSignature + " to:"); 192 logWriter.println("====> package org.apache.harmony.jpda.tests.jdwp.VirtualMachine;"); 193 logWriter.println("====> class RedefineClass_Debuggee {"); 194 logWriter.println("====> static String testMethod() {"); 195 logWriter.println("====> return \"testMethod_Result_After_Redefine\";"); 196 logWriter.println("====> }"); 197 logWriter.println("====> }"); 198 return Base64.getDecoder().decode( 199 "ZGV4CjAzNQAVa34RK7cHNNGCveP4LGffC0tLZFeb7KuUAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAJ" + 200 "AAAAcAAAAAQAAACUAAAAAgAAAKQAAAAAAAAAAAAAAAMAAAC8AAAAAQAAANQAAACgAQAA9AAAACQB" + 201 "AAAsAQAALwEAAEMBAABXAQAAowEAAMABAADDAQAAzwEAAAIAAAADAAAABAAAAAYAAAABAAAAAQAA" + 202 "AAAAAAAGAAAAAwAAAAAAAAAAAAEAAAAAAAIAAQAAAAAAAgAAAAcAAAACAAAAAAAAAAAAAAAAAAAA" + 203 "BQAAAAAAAAD7AQAAAAAAAAEAAQABAAAA8QEAAAQAAABwEAAAAAAOAAEAAAAAAAAA9gEAAAMAAAAa" + 204 "AAgAEQAAAAY8aW5pdD4AAUwAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" + 205 "AEpMb3JnL2FwYWNoZS9oYXJtb255L2pwZGEvdGVzdHMvamR3cC9WaXJ0dWFsTWFjaGluZS9SZWRl" + 206 "ZmluZUNsYXNzX0RlYnVnZ2VlOwAbUmVkZWZpbmVDbGFzc19EZWJ1Z2dlZS5qYXZhAAFWAAp0ZXN0" + 207 "TWV0aG9kACB0ZXN0TWV0aG9kX1Jlc3VsdF9BZnRlcl9SZWRlZmluZQADAAcOAAYABw4AAAACAAGA" + 208 "gAT0AQEIjAIAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAkAAABwAAAAAgAAAAQAAACUAAAAAwAAAAIA" + 209 "AACkAAAABQAAAAMAAAC8AAAABgAAAAEAAADUAAAAASAAAAIAAAD0AAAAAiAAAAkAAAAkAQAAAyAA" + 210 "AAIAAADxAQAAACAAAAEAAAD7AQAAABAAAAEAAAAMAgAA"); 211 } 212 getNewClassBytes()213 private byte[] getNewClassBytes() { 214 if (debuggeeWrapper.vmMirror.canRedefineClasses()) { 215 return getNewClassBytesClass(); 216 } else { 217 return getNewClassBytesDex(); 218 } 219 } 220 221 /** 222 * This testcase exercises VirtualMachine.RedefineClasses command. 223 * <BR>At first the test starts RedefineClassesDebuggee which invokes 224 * the 'testMethod()' of RedefineClass_Debuggee class and prints the string 225 * returned by this method before redefining. 226 * <BR> Then the test performs VirtualMachine.RedefineClasses command 227 * for RedefineClass_Debuggee class - the 'testMethod()' is redefined. 228 * Next, the debuggee invokes the 'testMethod()' again and it is expected 229 * that the method returns another resulting string. 230 * The test checks that this resulting string is expected string. 231 */ testRedefineClasses001()232 public void testRedefineClasses001() { 233 thisTestName = "testClassObject001"; 234 235 //check capability, relevant for this test 236 logWriter.println("=> Check capability: canRedefineClasses"); 237 if (!debuggeeWrapper.vmMirror.canRedefineClasses() && 238 !debuggeeWrapper.vmMirror.canRedefineDexClasses()) { 239 logWriter.println("##WARNING: this VM doesn't possess capability: canRedefineClasses"); 240 return; 241 } 242 243 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": START..."); 244 synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY); 245 logWriter.println 246 ("\n=> Send VirtualMachine::ClassesBySignature command and and get checked class referenceTypeID..."); 247 logWriter.println("=> checkedClassSignature = " + checkedClassSignature); 248 CommandPacket classesBySignatureCommand = new CommandPacket( 249 JDWPCommands.VirtualMachineCommandSet.CommandSetID, 250 JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand); 251 classesBySignatureCommand.setNextValueAsString(checkedClassSignature); 252 253 ReplyPacket classesBySignatureReply = debuggeeWrapper.vmMirror.performCommand(classesBySignatureCommand); 254 classesBySignatureCommand = null; 255 256 checkReplyPacket(classesBySignatureReply, "VirtualMachine::ClassesBySignature command"); 257 258 int returnedClassesNumber = classesBySignatureReply.getNextValueAsInt(); 259 logWriter.println("=> ReturnedClassesNumber = " + returnedClassesNumber); 260 if ( returnedClassesNumber != 1 ) { 261 // Number of returned reference types - is NOt used here 262 printErrorAndFail("Unexpected number of classes is returned: " + 263 returnedClassesNumber + 264 ", Expected number = 1"); 265 } 266 267 classesBySignatureReply.getNextValueAsByte(); 268 // refTypeTag of class - is NOt used here 269 270 long refTypeID = classesBySignatureReply.getNextValueAsReferenceTypeID(); 271 classesBySignatureReply = null; 272 273 logWriter.println("=> Checked class referenceTypeID = " + refTypeID); 274 275 logWriter.println("\n=> Preparing info for " + thisCommandName); 276 byte[] newClass = getNewClassBytes(); 277 if (newClass == null) { 278 // Wasn't able to get new file. Just continue and return. 279 synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE); 280 return; 281 } 282 283 CommandPacket checkedCommand = new CommandPacket( 284 JDWPCommands.VirtualMachineCommandSet.CommandSetID, 285 JDWPCommands.VirtualMachineCommandSet.RedefineClassesCommand); 286 checkedCommand.setNextValueAsInt(1); // number of classes to redefine 287 checkedCommand.setNextValueAsReferenceTypeID(refTypeID); 288 checkedCommand.setNextValueAsInt(newClass.length); 289 for (byte currentByte: newClass) { 290 checkedCommand.setNextValueAsByte(currentByte); 291 } 292 logWriter.println("=> Number of written bytes as new class file = " + newClass.length); 293 logWriter.println("\n=> Send " + thisCommandName + " and check reply..."); 294 295 ReplyPacket checkedReply = debuggeeWrapper.vmMirror.performCommand(checkedCommand); 296 checkedCommand = null; 297 int[] expectedErrors = { 298 JDWPConstants.Error.NOT_IMPLEMENTED, 299 }; 300 short errorCode = checkedReply.getErrorCode(); 301 if ( errorCode != JDWPConstants.Error.NONE ) { 302 if ( errorCode != JDWPConstants.Error.UNSUPPORTED_VERSION ) { 303 finalSyncMessage = JPDADebuggeeSynchronizer.SGNL_CONTINUE; 304 printErrorAndFail( 305 "## WARNING: A class file for redefine has a version number not supported by this VM" + 306 "\n## It should be re-created"); 307 } 308 boolean expectedErr = false; 309 for (int i=0; i < expectedErrors.length; i++) { 310 if ( errorCode == expectedErrors[i] ) { 311 expectedErr = true; 312 break; 313 } 314 } 315 if ( expectedErr ) { 316 logWriter.println("=> " + thisCommandName 317 + " returns expected ERROR = " + errorCode 318 + "(" + JDWPConstants.Error.getName(errorCode) + ")"); 319 logWriter.println 320 ("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 321 return; 322 } else { 323 finalSyncMessage = JPDADebuggeeSynchronizer.SGNL_CONTINUE; 324 printErrorAndFail(thisCommandName 325 + " returns unexpected ERROR = " + errorCode 326 + "(" + JDWPConstants.Error.getName(errorCode) + ")"); 327 } 328 } 329 logWriter.println("=> " + thisCommandName + " returns reply without any error"); 330 331 assertAllDataRead(checkedReply); 332 333 logWriter.println("\n=> Send Debuggee signal to continue and execute redefined testMethod"); 334 synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE); 335 336 String testMethodResult = synchronizer.receiveMessage(); 337 logWriter.println("=> Redefined testMethod result = \"" + testMethodResult + "\""); 338 if ( testMethodResult.equals("testMethod_Result_After_Redefine") ) { 339 logWriter.println("=> OK - it is expected result"); 340 } else { 341 printErrorAndFail("it is NOT expected result" + 342 "\n Expected result = \"testMethod_Result_After_Redefine\""); 343 } 344 345 logWriter.println("==> " + thisTestName + " for " + thisCommandName + ": FINISH"); 346 } 347 } 348