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 std::vector<TString> orderedKeys = Values.GetKeys(); 88 89 for (std::vector<TString>::const_iterator iterator = orderedKeys.begin(); 90 iterator != orderedKeys.end(); iterator++) { 91 TString name = *iterator; 92 TString value; 93 94 if (Values.GetValue(name, value) == true) { 95 AppendValue(name, value); 96 } 97 } 98 } 99 100 void JavaOptions::ReplaceValue(const TString Key, TString Value) { 101 for (std::list<JavaOptionItem>::iterator iterator = FItems.begin(); 102 iterator != FItems.end(); iterator++) { 103 104 TString lkey = iterator->name; 105 106 if (lkey == Key) { 107 JavaOptionItem item = *iterator; 108 item.value = Value; 109 iterator = FItems.erase(iterator); 110 FItems.insert(iterator, item); 111 break; 112 } 113 } 114 } 115 116 std::list<TString> JavaOptions::ToList() { 117 std::list<TString> result; 118 Macros& macros = Macros::GetInstance(); 119 120 for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin(); 121 iterator != FItems.end(); iterator++) { 122 TString key = iterator->name; 123 TString value = iterator->value; 124 TString option = Helpers::NameValueToString(key, value); 125 option = macros.ExpandMacros(option); 126 result.push_back(option); 127 } 128 129 return result; 130 } 131 132 size_t JavaOptions::GetCount() { 133 return FItems.size(); 134 } 135 136 //---------------------------------------------------------------------------- 137 138 JavaVirtualMachine::JavaVirtualMachine() { 139 } 140 141 JavaVirtualMachine::~JavaVirtualMachine(void) { 142 } 143 144 bool JavaVirtualMachine::StartJVM() { 145 Platform& platform = Platform::GetInstance(); 146 Package& package = Package::GetInstance(); 147 148 TString classpath = package.GetClassPath(); 149 TString modulepath = package.GetModulePath(); 150 JavaOptions options; 151 152 if (modulepath.empty() == false) { 153 options.AppendValue(_T("-Djava.module.path"), modulepath); 154 } 155 156 options.AppendValue(_T("-Djava.library.path"), 157 package.GetPackageAppDirectory() + FilePath::PathSeparator() 158 + package.GetPackageLauncherDirectory()); 159 options.AppendValue( 160 _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); 161 options.AppendValues(package.GetJavaOptions()); 162 163 #ifdef DEBUG 164 if (package.Debugging() == dsJava) { 165 options.AppendValue(_T("-Xdebug"), _T("")); 166 options.AppendValue( 167 _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"), 168 _T("")); 169 platform.ShowMessage(_T("localhost:5005")); 170 } 171 #endif // DEBUG 172 173 TString maxHeapSizeOption; 174 TString minHeapSizeOption; 175 176 177 if (package.GetMemoryState() == PackageBootFields::msAuto) { 178 TPlatformNumber memorySize = package.GetMemorySize(); 179 TString memory = 180 PlatformString((size_t)memorySize).toString() + _T("m"); 181 maxHeapSizeOption = TString(_T("-Xmx")) + memory; 182 options.AppendValue(maxHeapSizeOption, _T("")); 183 184 if (memorySize > 256) 185 minHeapSizeOption = _T("-Xms256m"); 186 else 187 minHeapSizeOption = _T("-Xms") + memory; 188 189 options.AppendValue(minHeapSizeOption, _T("")); 190 } 191 192 TString mainClassName = package.GetMainClassName(); 193 TString mainModule = package.GetMainModule(); 194 195 if (mainClassName.empty() == true && mainModule.empty() == true) { 196 Messages& messages = Messages::GetInstance(); 197 platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED)); 198 return false; 199 } 200 201 configureLibrary(); 202 203 // Initialize the arguments to JLI_Launch() 204 // 205 // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. 206 // This new thread simply re-runs main(argc, argv). Therefore we do not 207 // want to add new args if we are still in the original main thread so we 208 // will treat them as command line args provided by the user ... 209 // Only propagate original set of args first time. 210 211 options.AppendValue(_T("-classpath")); 212 options.AppendValue(classpath); 213 214 std::list<TString> vmargs; 215 vmargs.push_back(package.GetCommandName()); 216 217 if (package.HasSplashScreen() == true) { 218 options.AppendValue(TString(_T("-splash:")) 219 + package.GetSplashScreenFileName(), _T("")); 220 } 221 222 if (mainModule.empty() == true) { 223 options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), 224 _T("")); 225 } else { 226 options.AppendValue(_T("-m")); 227 options.AppendValue(mainModule); 228 } 229 230 return launchVM(options, vmargs); 231 } 232 233 void JavaVirtualMachine::configureLibrary() { 234 Platform& platform = Platform::GetInstance(); 235 Package& package = Package::GetInstance(); 236 TString libName = package.GetJavaLibraryFileName(); 237 platform.addPlatformDependencies(&javaLibrary); 238 javaLibrary.Load(libName); 239 } 240 241 bool JavaVirtualMachine::launchVM(JavaOptions& options, 242 std::list<TString>& vmargs) { 243 Platform& platform = Platform::GetInstance(); 244 Package& package = Package::GetInstance(); 245 246 #ifdef MAC 247 // Mac adds a ProcessSerialNumber to args when launched from .app 248 // filter out the psn since they it's not expected in the app 249 if (platform.IsMainThread() == false) { 250 std::list<TString> loptions = options.ToList(); 251 vmargs.splice(vmargs.end(), loptions, 252 loptions.begin(), loptions.end()); 253 } 254 #else 255 std::list<TString> loptions = options.ToList(); 256 vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); 257 #endif 258 259 std::list<TString> largs = package.GetArgs(); 260 vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); 261 262 size_t argc = vmargs.size(); 263 DynamicBuffer<char*> argv(argc + 1); 264 if (argv.GetData() == NULL) { 265 return false; 266 } 267 268 unsigned int index = 0; 269 for (std::list<TString>::const_iterator iterator = vmargs.begin(); 270 iterator != vmargs.end(); iterator++) { 271 TString item = *iterator; 272 std::string arg = PlatformString(item).toStdString(); 273 #ifdef DEBUG 274 printf("%i %s\n", index, arg.c_str()); 275 #endif // DEBUG 276 argv[index] = PlatformString::duplicate(arg.c_str()); 277 index++; 278 } 279 280 argv[argc] = NULL; 281 282 // On Mac we can only free the boot fields if the calling thread is 283 // not the main thread. 284 #ifdef MAC 285 if (platform.IsMainThread() == false) { 286 package.FreeBootFields(); 287 } 288 #else 289 package.FreeBootFields(); 290 #endif // MAC 291 292 if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) { 293 return true; 294 } 295 296 for (index = 0; index < argc; index++) { 297 if (argv[index] != NULL) { 298 delete[] argv[index]; 299 } 300 } 301 302 return false; 303 }