1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 34 #include "JavaVirtualMachine.h" 35 #include "Platform.h" 36 #include "PlatformString.h" 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 154 void AppendValue(const TString Key, TString Value) { 155 AppendValue(Key, Value, NULL); 156 } 157 158 void AppendValue(const TString Key) { 159 AppendValue(Key, _T(""), NULL); 160 } 161 162 void AppendValues(OrderedMap<TString, TString> Values) { 163 std::vector<TString> orderedKeys = Values.GetKeys(); 164 165 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); iterator != orderedKeys.end(); iterator++) { 166 TString name = *iterator; 167 TString value; 168 169 if (Values.GetValue(name, value) == true) { 170 AppendValue(name, value); 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. 226 TString::iterator i = name.end(); 227 i--; 228 229 if (*i == '=') { 230 name = name.substr(0, name.size() - 1); 231 } 232 else { 233 i = value.begin(); 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()); 274 options.AppendValues(package.GetJVMArgs()); 275 options.AppendValues(RemoveTrailingEquals(package.GetJVMUserArgs())); 276 277 #ifdef DEBUG 278 if (package.Debugging() == dsJava) { 279 options.AppendValue(_T("-Xdebug"), _T("")); 280 options.AppendValue(_T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), _T("")); 281 platform.ShowMessage(_T("localhost:5005")); 282 } 283 #endif //DEBUG 284 285 TString maxHeapSizeOption; 286 TString minHeapSizeOption; 287 288 289 if (package.GetMemoryState() == PackageBootFields::msAuto) { 290 TPlatformNumber memorySize = package.GetMemorySize(); 291 TString memory = PlatformString((size_t)memorySize).toString() + _T("m"); 292 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 293 options.AppendValue(maxHeapSizeOption, _T("")); 294 295 if (memorySize > 256) 296 minHeapSizeOption = _T("-Xms256m"); 297 else 298 minHeapSizeOption = _T("-Xms") + memory; 299 300 options.AppendValue(minHeapSizeOption, _T("")); 301 } 302 303 TString mainClassName = package.GetMainClassName(); 304 TString mainModule = package.GetMainModule(); 305 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("")); 346 } 347 else { 348 options.AppendValue(_T("-m")); 349 options.AppendValue(mainModule); 350 } 351 352 #ifdef MAC 353 // Mac adds a ProcessSerialNumber to args when launched from .app 354 // filter out the psn since they it's not expected in the app 355 if (platform.IsMainThread() == false) { 356 std::list<TString> loptions = options.ToList(); 357 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 358 } 359 #else 360 std::list<TString> loptions = options.ToList(); 361 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 362 #endif 363 364 std::list<TString> largs = package.GetArgs(); 365 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 366 size_t argc = vmargs.size(); 367 DynamicBuffer<char*> argv(argc + 1); 368 unsigned int index = 0; 369 370 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 371 iterator != vmargs.end(); iterator++) { 372 TString item = *iterator; 373 std::string arg = PlatformString(item).toStdString(); 374 #ifdef DEBUG 375 printf("%i %s\n", index, arg.c_str()); 376 #endif //DEBUG 377 argv[index] = PlatformString::duplicate(arg.c_str()); 378 index++; 379 } 380 381 argv[argc] = NULL; 382 383 // On Mac we can only free the boot fields if the calling thread is not the main thread. 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 }