37 #include "FilePath.h" 38 #include "Package.h" 39 #include "Java.h" 40 #include "Helpers.h" 41 #include "Messages.h" 42 #include "Macros.h" 43 #include "PlatformThread.h" 44 45 #include "jni.h" 46 47 #include <map> 48 #include <list> 49 50 51 bool RunVM() { 52 bool result = false; 53 JavaVirtualMachine javavm; 54 55 if (javavm.StartJVM() == true) { 56 result = true; 57 javavm.ShutdownJVM(); 58 } 59 else { 60 Platform& platform = Platform::GetInstance(); 61 platform.ShowMessage(_T("Failed to launch JVM\n")); 62 } 63 64 return result; 65 } 66 67 68 // Private typedef for function pointer casting 69 #ifndef USE_JLI_LAUNCH 70 #define LAUNCH_FUNC "JNI_CreateJavaVM" 71 typedef jint (JNICALL *JVM_CREATE)(JavaVM ** jvm, JNIEnv ** env, void *); 72 #else 73 #define LAUNCH_FUNC "JLI_Launch" 74 typedef int (JNICALL *JVM_CREATE)(int argc, char ** argv, 75 int jargc, const char** jargv, 76 int appclassc, const char** appclassv, 77 const char* fullversion, 78 const char* dotversion, 79 const char* pname, 80 const char* lname, 81 jboolean javaargs, 82 jboolean cpwildcard, 83 jboolean javaw, 84 jint ergo); 85 #endif //USE_JLI_LAUNCH 86 87 class JavaLibrary : public Library { 88 JVM_CREATE FCreateProc; 89 90 JavaLibrary(const TString &FileName); 91 92 public: 93 JavaLibrary() : Library() { 94 FCreateProc = NULL; 95 } 96 97 #ifndef USE_JLI_LAUNCH 98 bool JavaVMCreate(JavaVM** jvm, JNIEnv** env, void* jvmArgs) { 99 bool result = true; 100 101 if (FCreateProc == NULL) { 102 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 103 } 104 105 if (FCreateProc == NULL) { 106 Platform& platform = Platform::GetInstance(); 107 Messages& messages = Messages::GetInstance(); 108 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 109 return false; 110 } 111 112 if ((*FCreateProc)(jvm, env, jvmArgs) < 0) { 113 Platform& platform = Platform::GetInstance(); 114 Messages& messages = Messages::GetInstance(); 115 platform.ShowMessage(messages.GetMessage(FAILED_CREATING_JVM)); 116 return false; 117 } 118 119 return result; 120 } 121 #else 122 bool JavaVMCreate(size_t argc, char *argv[]) { 123 if (FCreateProc == NULL) { 124 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 125 } 126 127 if (FCreateProc == NULL) { 128 Platform& platform = Platform::GetInstance(); 129 Messages& messages = Messages::GetInstance(); 130 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 131 return false; 132 } 133 134 return FCreateProc((int)argc, argv, 135 0, NULL, 136 0, NULL, 137 "", 138 "", 139 "java", 140 "java", 141 false, 142 false, 143 false, 144 0) == 0; 145 } 146 #endif //USE_JLI_LAUNCH 147 }; 148 149 #ifndef USE_JLI_LAUNCH 150 //debug hook to print JVM messages into console. 151 static jint JNICALL vfprintfHook(FILE *fp, const char *format, va_list args) { 152 #ifdef WINDOWS 153 char buffer[20480]; 154 int len; 155 HANDLE hConsole; 156 DWORD wasWritten; 157 158 len = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), format, args); 159 160 if (len <= 0) { 161 return len; 162 } 163 164 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 165 166 if (hConsole == INVALID_HANDLE_VALUE) { 167 return false; 168 } 169 170 //JVM will always pass us ASCII 171 WriteConsoleA(hConsole, buffer, strlen(buffer), &wasWritten, NULL); 172 173 return (jint) len; 174 #endif //WINDOWS 175 #ifdef LINUX 176 return 0; 177 #endif //LINUX 178 } 179 #endif //USE_JLI_LAUNCH 180 181 //-------------------------------------------------------------------------------------------------- 182 183 struct JavaOptionItem { 184 TString name; 185 TString value; 186 void* extraInfo; 187 }; 188 189 190 class JavaOptions { 191 private: 192 std::list<JavaOptionItem> FItems; 193 JavaVMOption* FOptions; 194 195 public: 196 JavaOptions() { 197 FOptions = NULL; 198 199 #ifndef USE_JLI_LAUNCH 200 #ifdef DEBUG 201 Platform& platform = Platform::GetInstance(); 202 203 if (platform.GetDebugState() == dsNative) { 204 AppendValue(_T("vfprintf"), _T(""), (void*)vfprintfHook); 205 } 206 #endif //DEBUG 207 #endif //USE_JLI_LAUNCH 208 } 209 210 ~JavaOptions() { 211 if (FOptions != NULL) { 212 for (unsigned int index = 0; index < GetCount(); index++) { 213 delete[] FOptions[index].optionString; 214 } 215 216 delete[] FOptions; 217 } 218 } 219 220 void AppendValue(const TString Key, TString Value, void* Extra) { 221 JavaOptionItem item; 222 item.name = Key; 223 item.value = Value; 224 item.extraInfo = Extra; 225 FItems.push_back(item); 226 } 227 245 } 246 } 247 } 248 249 void ReplaceValue(const TString Key, TString Value) { 250 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 251 iterator != FItems.end(); iterator++) { 252 253 TString lkey = iterator->name; 254 255 if (lkey == Key) { 256 JavaOptionItem item = *iterator; 257 item.value = Value; 258 iterator = FItems.erase(iterator); 259 FItems.insert(iterator, item); 260 break; 261 } 262 } 263 } 264 265 #ifndef USE_JLI_LAUNCH 266 JavaVMOption* ToJavaOptions() { 267 FOptions = new JavaVMOption[FItems.size()]; 268 memset(FOptions, 0, sizeof(JavaVMOption) * FItems.size()); 269 Macros& macros = Macros::GetInstance(); 270 unsigned int index = 0; 271 272 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 273 iterator != FItems.end(); iterator++) { 274 TString key = iterator->name; 275 TString value = iterator->value; 276 TString option = Helpers::NameValueToString(key, value); 277 option = macros.ExpandMacros(option); 278 #ifdef DEBUG 279 printf("%s\n", PlatformString(option).c_str()); 280 #endif //DEBUG 281 FOptions[index].optionString = PlatformString::duplicate(PlatformString(option).c_str()); 282 FOptions[index].extraInfo = iterator->extraInfo; 283 index++; 284 } 285 286 return FOptions; 287 } 288 #else 289 std::list<TString> ToList() { 290 std::list<TString> result; 291 Macros& macros = Macros::GetInstance(); 292 293 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 294 iterator != FItems.end(); iterator++) { 295 TString key = iterator->name; 296 TString value = iterator->value; 297 TString option = Helpers::NameValueToString(key, value); 298 option = macros.ExpandMacros(option); 299 result.push_back(option); 300 } 301 302 return result; 303 } 304 #endif //USE_JLI_LAUNCH 305 306 size_t GetCount() { 307 return FItems.size(); 308 } 309 }; 310 311 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use 312 // other parts of the launcher. 313 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) { 314 OrderedMap<TString, TString> result; 315 316 std::vector<TString> keys = Map.GetKeys(); 317 318 for (size_t index = 0; index < keys.size(); index++) { 319 TString name = keys[index]; 320 TString value; 321 322 if (Map.GetValue(name, value) == true) { 323 // If the last character of the key is an equals, then remove it. If there is no 324 // equals then combine the two as a key. 333 334 if (*i == '=') { 335 value = value.substr(1, value.size() - 1); 336 } 337 else { 338 name = name + value; 339 value = _T(""); 340 } 341 } 342 343 result.Append(name, value); 344 } 345 } 346 347 return result; 348 } 349 350 //-------------------------------------------------------------------------------------------------- 351 352 JavaVirtualMachine::JavaVirtualMachine() { 353 #ifndef USE_JLI_LAUNCH 354 FEnv = NULL; 355 FJvm = NULL; 356 #endif //USE_JLI_LAUNCH 357 } 358 359 JavaVirtualMachine::~JavaVirtualMachine(void) { 360 } 361 362 bool JavaVirtualMachine::StartJVM() { 363 Platform& platform = Platform::GetInstance(); 364 Package& package = Package::GetInstance(); 365 366 TString classpath = package.GetClassPath(); 367 TString modulepath = package.GetModulePath(); 368 JavaOptions options; 369 370 if (modulepath.empty() == false) { 371 options.AppendValue(_T("-Djava.module.path"), modulepath); 372 } 373 374 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 375 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 376 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 409 if (mainClassName.empty() == true && mainModule.empty() == true) { 410 Messages& messages = Messages::GetInstance(); 411 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 412 return false; 413 } 414 415 JavaLibrary javaLibrary; 416 417 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file fails in WindowsPlatform.cpp on the check to 418 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 419 #ifdef _WIN64 420 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 421 javaLibrary.AddDependency(_T("msvcr100.dll")); 422 } 423 #else 424 javaLibrary.AddDependencies(platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(package.GetJVMLibraryFileName()))); 425 #endif 426 427 javaLibrary.Load(package.GetJVMLibraryFileName()); 428 429 #ifndef USE_JLI_LAUNCH 430 options.AppendValue(_T("-Djava.class.path"), classpath); 431 432 if (package.HasSplashScreen() == true) { 433 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 434 } 435 436 // Set up the VM init args 437 JavaVMInitArgs jvmArgs; 438 memset(&jvmArgs, 0, sizeof(JavaVMInitArgs)); 439 jvmArgs.version = JNI_VERSION_1_6; 440 jvmArgs.options = options.ToJavaOptions(); 441 jvmArgs.nOptions = (jint)options.GetCount(); 442 jvmArgs.ignoreUnrecognized = JNI_TRUE; 443 444 if (javaLibrary.JavaVMCreate(&FJvm, &FEnv, &jvmArgs) == true) { 445 try { 446 JavaClass mainClass(FEnv, Helpers::ConvertIdToJavaPath(mainClassName)); 447 JavaStaticMethod mainMethod = mainClass.GetStaticMethod(_T("main"), _T("([Ljava/lang/String;)V")); 448 std::list<TString> appargs = package.GetArgs(); 449 JavaStringArray largs(FEnv, appargs); 450 451 package.FreeBootFields(); 452 453 mainMethod.CallVoidMethod(1, largs.GetData()); 454 return true; 455 } 456 catch (JavaException& exception) { 457 platform.ShowMessage(exception.GetMessage()); 458 return false; 459 } 460 } 461 462 return false; 463 } 464 #else 465 // Initialize the arguments to JLI_Launch() 466 // 467 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This 468 // new thread simply re-runs main(argc, argv). Therefore we do not want 469 // to add new args if we are still in the original main thread so we 470 // will treat them as command line args provided by the user ... 471 // Only propagate original set of args first time. 472 473 options.AppendValue(_T("-classpath")); 474 options.AppendValue(classpath); 475 476 std::list<TString> vmargs; 477 vmargs.push_back(package.GetCommandName()); 478 479 if (package.HasSplashScreen() == true) { 480 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 481 } 482 483 if (mainModule.empty() == true) { 484 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T("")); 523 #ifdef MAC 524 if (platform.IsMainThread() == false) { 525 package.FreeBootFields(); 526 } 527 #else 528 package.FreeBootFields(); 529 #endif //MAC 530 531 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 532 return true; 533 } 534 535 for (index = 0; index < argc; index++) { 536 if (argv[index] != NULL) { 537 delete[] argv[index]; 538 } 539 } 540 541 return false; 542 } 543 #endif //USE_JLI_LAUNCH 544 545 void JavaVirtualMachine::ShutdownJVM() { 546 #ifndef USE_JLI_LAUNCH 547 if (FJvm != NULL) { 548 // If application main() exits quickly but application is run on some other thread 549 // (e.g. Swing app performs invokeLater() in main and exits) 550 // then if we return execution to tWinMain it will exit. 551 // This will cause process to exit and application will not actually run. 552 // 553 // To avoid this we are trying to detach jvm from current thread (java.exe does the same) 554 // Because we are doing this on the main JVM thread (i.e. one that was used to create JVM) 555 // this call will spawn "Destroy Java VM" java thread that will shut JVM once there are 556 // no non-daemon threads running, and then return control here. 557 // I.e. this will happen when EDT and other app thread will exit. 558 if (FJvm->DetachCurrentThread() != JNI_OK) { 559 Platform& platform = Platform::GetInstance(); 560 platform.ShowMessage(_T("Detach failed.")); 561 } 562 563 FJvm->DestroyJavaVM(); 564 } 565 #endif //USE_JLI_LAUNCH 566 } | 37 #include "FilePath.h" 38 #include "Package.h" 39 #include "Java.h" 40 #include "Helpers.h" 41 #include "Messages.h" 42 #include "Macros.h" 43 #include "PlatformThread.h" 44 45 #include "jni.h" 46 47 #include <map> 48 #include <list> 49 50 51 bool RunVM() { 52 bool result = false; 53 JavaVirtualMachine javavm; 54 55 if (javavm.StartJVM() == true) { 56 result = true; 57 } 58 else { 59 Platform& platform = Platform::GetInstance(); 60 platform.ShowMessage(_T("Failed to launch JVM\n")); 61 } 62 63 return result; 64 } 65 66 67 // Private typedef for function pointer casting 68 #define LAUNCH_FUNC "JLI_Launch" 69 typedef int (JNICALL *JVM_CREATE)(int argc, char ** argv, 70 int jargc, const char** jargv, 71 int appclassc, const char** appclassv, 72 const char* fullversion, 73 const char* dotversion, 74 const char* pname, 75 const char* lname, 76 jboolean javaargs, 77 jboolean cpwildcard, 78 jboolean javaw, 79 jint ergo); 80 81 class JavaLibrary : public Library { 82 JVM_CREATE FCreateProc; 83 84 JavaLibrary(const TString &FileName); 85 86 public: 87 JavaLibrary() : Library() { 88 FCreateProc = NULL; 89 } 90 91 bool JavaVMCreate(size_t argc, char *argv[]) { 92 if (FCreateProc == NULL) { 93 FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC); 94 } 95 96 if (FCreateProc == NULL) { 97 Platform& platform = Platform::GetInstance(); 98 Messages& messages = Messages::GetInstance(); 99 platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT)); 100 return false; 101 } 102 103 return FCreateProc((int)argc, argv, 104 0, NULL, 105 0, NULL, 106 "", 107 "", 108 "java", 109 "java", 110 false, 111 false, 112 false, 113 0) == 0; 114 } 115 }; 116 117 //-------------------------------------------------------------------------------------------------- 118 119 struct JavaOptionItem { 120 TString name; 121 TString value; 122 void* extraInfo; 123 }; 124 125 126 class JavaOptions { 127 private: 128 std::list<JavaOptionItem> FItems; 129 JavaVMOption* FOptions; 130 131 public: 132 JavaOptions() { 133 FOptions = NULL; 134 } 135 136 ~JavaOptions() { 137 if (FOptions != NULL) { 138 for (unsigned int index = 0; index < GetCount(); index++) { 139 delete[] FOptions[index].optionString; 140 } 141 142 delete[] FOptions; 143 } 144 } 145 146 void AppendValue(const TString Key, TString Value, void* Extra) { 147 JavaOptionItem item; 148 item.name = Key; 149 item.value = Value; 150 item.extraInfo = Extra; 151 FItems.push_back(item); 152 } 153 171 } 172 } 173 } 174 175 void ReplaceValue(const TString Key, TString Value) { 176 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 177 iterator != FItems.end(); iterator++) { 178 179 TString lkey = iterator->name; 180 181 if (lkey == Key) { 182 JavaOptionItem item = *iterator; 183 item.value = Value; 184 iterator = FItems.erase(iterator); 185 FItems.insert(iterator, item); 186 break; 187 } 188 } 189 } 190 191 std::list<TString> ToList() { 192 std::list<TString> result; 193 Macros& macros = Macros::GetInstance(); 194 195 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 196 iterator != FItems.end(); iterator++) { 197 TString key = iterator->name; 198 TString value = iterator->value; 199 TString option = Helpers::NameValueToString(key, value); 200 option = macros.ExpandMacros(option); 201 result.push_back(option); 202 } 203 204 return result; 205 } 206 207 size_t GetCount() { 208 return FItems.size(); 209 } 210 }; 211 212 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use 213 // other parts of the launcher. 214 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) { 215 OrderedMap<TString, TString> result; 216 217 std::vector<TString> keys = Map.GetKeys(); 218 219 for (size_t index = 0; index < keys.size(); index++) { 220 TString name = keys[index]; 221 TString value; 222 223 if (Map.GetValue(name, value) == true) { 224 // If the last character of the key is an equals, then remove it. If there is no 225 // equals then combine the two as a key. 234 235 if (*i == '=') { 236 value = value.substr(1, value.size() - 1); 237 } 238 else { 239 name = name + value; 240 value = _T(""); 241 } 242 } 243 244 result.Append(name, value); 245 } 246 } 247 248 return result; 249 } 250 251 //-------------------------------------------------------------------------------------------------- 252 253 JavaVirtualMachine::JavaVirtualMachine() { 254 } 255 256 JavaVirtualMachine::~JavaVirtualMachine(void) { 257 } 258 259 bool JavaVirtualMachine::StartJVM() { 260 Platform& platform = Platform::GetInstance(); 261 Package& package = Package::GetInstance(); 262 263 TString classpath = package.GetClassPath(); 264 TString modulepath = package.GetModulePath(); 265 JavaOptions options; 266 267 if (modulepath.empty() == false) { 268 options.AppendValue(_T("-Djava.module.path"), modulepath); 269 } 270 271 options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); 272 options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 273 options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); 306 if (mainClassName.empty() == true && mainModule.empty() == true) { 307 Messages& messages = Messages::GetInstance(); 308 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 309 return false; 310 } 311 312 JavaLibrary javaLibrary; 313 314 // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file fails in WindowsPlatform.cpp on the check to 315 // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) 316 #ifdef _WIN64 317 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 318 javaLibrary.AddDependency(_T("msvcr100.dll")); 319 } 320 #else 321 javaLibrary.AddDependencies(platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(package.GetJVMLibraryFileName()))); 322 #endif 323 324 javaLibrary.Load(package.GetJVMLibraryFileName()); 325 326 // Initialize the arguments to JLI_Launch() 327 // 328 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This 329 // new thread simply re-runs main(argc, argv). Therefore we do not want 330 // to add new args if we are still in the original main thread so we 331 // will treat them as command line args provided by the user ... 332 // Only propagate original set of args first time. 333 334 options.AppendValue(_T("-classpath")); 335 options.AppendValue(classpath); 336 337 std::list<TString> vmargs; 338 vmargs.push_back(package.GetCommandName()); 339 340 if (package.HasSplashScreen() == true) { 341 options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); 342 } 343 344 if (mainModule.empty() == true) { 345 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T("")); 384 #ifdef MAC 385 if (platform.IsMainThread() == false) { 386 package.FreeBootFields(); 387 } 388 #else 389 package.FreeBootFields(); 390 #endif //MAC 391 392 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 393 return true; 394 } 395 396 for (index = 0; index < argc; index++) { 397 if (argv[index] != NULL) { 398 delete[] argv[index]; 399 } 400 } 401 402 return false; 403 } |