1 /* 2 * Copyright (c) 2014, 2019, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "JavaVirtualMachine.h" 27 #include "Platform.h" 28 #include "PlatformString.h" 29 #include "FilePath.h" 30 #include "Package.h" 31 #include "Helpers.h" 32 #include "Messages.h" 33 #include "Macros.h" 34 35 #include "jni.h" 36 37 #include <map> 38 #include <list> 39 #include <sstream> 40 41 42 bool RunVM() { 43 JavaVirtualMachine javavm; 44 45 bool result = javavm.StartJVM(); 46 47 if (!result) { 48 Platform& platform = Platform::GetInstance(); 49 platform.ShowMessage(_T("Failed to launch JVM\n")); 50 } 51 52 return result; 53 } 54 55 //---------------------------------------------------------------------------- 56 57 JavaOptions::JavaOptions(): FOptions(NULL) { 58 } 59 60 JavaOptions::~JavaOptions() { 61 if (FOptions != NULL) { 62 for (unsigned int index = 0; index < GetCount(); index++) { 63 delete[] FOptions[index].optionString; 64 } 65 66 delete[] FOptions; 67 } 68 } 69 70 void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) { 71 JavaOptionItem item; 72 item.name = Key; 73 item.value = Value; 74 item.extraInfo = Extra; 75 FItems.push_back(item); 76 } 77 78 void JavaOptions::AppendValue(const TString Key, TString Value) { 79 AppendValue(Key, Value, NULL); 80 } 81 82 void JavaOptions::AppendValue(const TString Key) { 83 AppendValue(Key, _T(""), NULL); 84 } 85 86 void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) { 87 if (Values.GetAllowDuplicates()) { 88 for (int i = 0; i < (int)Values.Count(); i++) { 89 TString name, value; 90 91 bool bResult = Values.GetKey(i, name); 92 bResult &= Values.GetValue(i, value); 93 94 if (bResult) { 95 AppendValue(name, value); 96 } 97 } 98 } else { // In case we asked to add values from OrderedMap with allow 99 // duplicates set to false. Not used now, but should avoid possible 100 // bugs. 101 std::vector<TString> orderedKeys = Values.GetKeys(); 102 103 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); 104 iterator != orderedKeys.end(); iterator++) { 105 TString name = *iterator; 106 TString value; 107 108 if (Values.GetValue(name, value) == true) { 109 AppendValue(name, value); 110 } 111 } 112 } 113 } 114 115 void JavaOptions::ReplaceValue(const TString Key, TString Value) { 116 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 117 iterator != FItems.end(); iterator++) { 118 119 TString lkey = iterator->name; 120 121 if (lkey == Key) { 122 JavaOptionItem item = *iterator; 123 item.value = Value; 124 iterator = FItems.erase(iterator); 125 FItems.insert(iterator, item); 126 break; 127 } 128 } 129 } 130 131 std::list<TString> JavaOptions::ToList() { 132 std::list<TString> result; 133 Macros& macros = Macros::GetInstance(); 134 135 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 136 iterator != FItems.end(); iterator++) { 137 TString key = iterator->name; 138 TString value = iterator->value; 139 TString option = Helpers::NameValueToString(key, value); 140 option = macros.ExpandMacros(option); 141 result.push_back(option); 142 } 143 144 return result; 145 } 146 147 size_t JavaOptions::GetCount() { 148 return FItems.size(); 149 } 150 151 //---------------------------------------------------------------------------- 152 153 JavaVirtualMachine::JavaVirtualMachine() { 154 } 155 156 JavaVirtualMachine::~JavaVirtualMachine(void) { 157 } 158 159 bool JavaVirtualMachine::StartJVM() { 160 Platform& platform = Platform::GetInstance(); 161 Package& package = Package::GetInstance(); 162 163 TString classpath = package.GetClassPath(); 164 TString modulepath = package.GetModulePath(); 165 JavaOptions options; 166 167 if (modulepath.empty() == false) { 168 options.AppendValue(_T("-Djava.module.path"), modulepath); 169 } 170 171 options.AppendValue(_T("-Djava.library.path"), 172 package.GetPackageAppDirectory() + FilePath::PathSeparator() 173 + package.GetPackageLauncherDirectory()); 174 options.AppendValue( 175 _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 176 options.AppendValues(package.GetJavaOptions()); 177 178 #ifdef DEBUG 179 if (package.Debugging() == dsJava) { 180 options.AppendValue(_T("-Xdebug"), _T("")); 181 options.AppendValue( 182 _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), 183 _T("")); 184 platform.ShowMessage(_T("localhost:5005")); 185 } 186 #endif // DEBUG 187 188 TString maxHeapSizeOption; 189 TString minHeapSizeOption; 190 191 192 if (package.GetMemoryState() == PackageBootFields::msAuto) { 193 TPlatformNumber memorySize = package.GetMemorySize(); 194 TString memory = 195 PlatformString((size_t)memorySize).toString() + _T("m"); 196 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 197 options.AppendValue(maxHeapSizeOption, _T("")); 198 199 if (memorySize > 256) 200 minHeapSizeOption = _T("-Xms256m"); 201 else 202 minHeapSizeOption = _T("-Xms") + memory; 203 204 options.AppendValue(minHeapSizeOption, _T("")); 205 } 206 207 TString mainClassName = package.GetMainClassName(); 208 TString mainModule = package.GetMainModule(); 209 210 if (mainClassName.empty() == true && mainModule.empty() == true) { 211 Messages& messages = Messages::GetInstance(); 212 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 213 return false; 214 } 215 216 configureLibrary(); 217 218 // Initialize the arguments to JLI_Launch() 219 // 220 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. 221 // This new thread simply re-runs main(argc, argv). Therefore we do not 222 // want to add new args if we are still in the original main thread so we 223 // will treat them as command line args provided by the user ... 224 // Only propagate original set of args first time. 225 226 options.AppendValue(_T("-classpath")); 227 options.AppendValue(classpath); 228 229 std::list<TString> vmargs; 230 vmargs.push_back(package.GetCommandName()); 231 232 if (package.HasSplashScreen() == true) { 233 options.AppendValue(TString(_T("-splash:")) 234 + package.GetSplashScreenFileName(), _T("")); 235 } 236 237 if (mainModule.empty() == true) { 238 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), 239 _T("")); 240 } else { 241 options.AppendValue(_T("-m")); 242 options.AppendValue(mainModule); 243 } 244 245 return launchVM(options, vmargs); 246 } 247 248 void JavaVirtualMachine::configureLibrary() { 249 Platform& platform = Platform::GetInstance(); 250 Package& package = Package::GetInstance(); 251 TString libName = package.GetJavaLibraryFileName(); 252 platform.addPlatformDependencies(&javaLibrary); 253 javaLibrary.Load(libName); 254 } 255 256 bool JavaVirtualMachine::launchVM(JavaOptions& options, 257 std::list<TString>& vmargs) { 258 Platform& platform = Platform::GetInstance(); 259 Package& package = Package::GetInstance(); 260 261 #ifdef MAC 262 // Mac adds a ProcessSerialNumber to args when launched from .app 263 // filter out the psn since they it's not expected in the app 264 if (platform.IsMainThread() == false) { 265 std::list<TString> loptions = options.ToList(); 266 vmargs.splice(vmargs.end(), loptions, 267 loptions.begin(), loptions.end()); 268 } 269 #else 270 std::list<TString> loptions = options.ToList(); 271 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 272 #endif 273 274 std::list<TString> largs = package.GetArgs(); 275 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 276 277 size_t argc = vmargs.size(); 278 DynamicBuffer<char*> argv(argc + 1); 279 if (argv.GetData() == NULL) { 280 return false; 281 } 282 283 unsigned int index = 0; 284 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 285 iterator != vmargs.end(); iterator++) { 286 TString item = *iterator; 287 std::string arg = PlatformString(item).toStdString(); 288 #ifdef DEBUG 289 printf("%i %s\n", index, arg.c_str()); 290 #endif // DEBUG 291 argv[index] = PlatformString::duplicate(arg.c_str()); 292 index++; 293 } 294 295 argv[argc] = NULL; 296 297 // On Mac we can only free the boot fields if the calling thread is 298 // not the main thread. 299 #ifdef MAC 300 if (platform.IsMainThread() == false) { 301 package.FreeBootFields(); 302 } 303 #else 304 package.FreeBootFields(); 305 #endif // MAC 306 307 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 308 return true; 309 } 310 311 for (index = 0; index < argc; index++) { 312 if (argv[index] != NULL) { 313 delete[] argv[index]; 314 } 315 } 316 317 return false; 318 }