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 "JavaTypes.h"
  32 #include "Helpers.h"
  33 #include "Messages.h"
  34 #include "Macros.h"
  35 #include "PlatformThread.h"
  36 
  37 #include "jni.h"
  38 
  39 #include <map>
  40 #include <list>
  41 #include <sstream>
  42 
  43 
  44 bool RunVM() {
  45     JavaVirtualMachine javavm;
  46 
  47     bool result = javavm.StartJVM();
  48 
  49     if (!result) {
  50         Platform& platform = Platform::GetInstance();
  51         platform.ShowMessage(_T("Failed to launch JVM\n"));
  52     }
  53 
  54     return result;
  55 }
  56 
  57 JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL)  {
  58 }
  59 
  60 bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) {
  61     if (FCreateProc == NULL) {
  62         FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC);
  63     }
  64 
  65     if (FCreateProc == NULL) {
  66         Platform& platform = Platform::GetInstance();
  67         Messages& messages = Messages::GetInstance();
  68         platform.ShowMessage(
  69                 messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT));
  70         return false;
  71     }
  72 
  73     return FCreateProc((int)argc, argv,
  74             0, NULL,
  75             0, NULL,
  76             "",
  77             "",
  78             "java",
  79             "java",
  80             false,
  81             false,
  82             false,
  83             0) == 0;
  84 }
  85 
  86 //----------------------------------------------------------------------------
  87 
  88 JavaOptions::JavaOptions(): FOptions(NULL) {
  89 }
  90 
  91 JavaOptions::~JavaOptions() {
  92     if (FOptions != NULL) {
  93         for (unsigned int index = 0; index < GetCount(); index++) {
  94             delete[] FOptions[index].optionString;
  95         }
  96 
  97         delete[] FOptions;
  98     }
  99 }
 100 
 101 void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) {
 102     JavaOptionItem item;
 103     item.name = Key;
 104     item.value = Value;
 105     item.extraInfo = Extra;
 106     FItems.push_back(item);
 107 }
 108 
 109 void JavaOptions::AppendValue(const TString Key, TString Value) {
 110     AppendValue(Key, Value, NULL);
 111 }
 112 
 113 void JavaOptions::AppendValue(const TString Key) {
 114     AppendValue(Key, _T(""), NULL);
 115 }
 116 
 117 void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) {
 118     std::vector<TString> orderedKeys = Values.GetKeys();
 119 
 120     for (std::vector<TString>::const_iterator iterator = orderedKeys.begin();
 121         iterator != orderedKeys.end(); iterator++) {
 122         TString name = *iterator;
 123         TString value;
 124 
 125         if (Values.GetValue(name, value) == true) {
 126             AppendValue(name, value);
 127         }
 128     }
 129 }
 130 
 131 void JavaOptions::ReplaceValue(const TString Key, TString Value) {
 132     for (std::list<JavaOptionItem>::iterator iterator = FItems.begin();
 133         iterator != FItems.end(); iterator++) {
 134 
 135         TString lkey = iterator->name;
 136 
 137         if (lkey == Key) {
 138             JavaOptionItem item = *iterator;
 139             item.value = Value;
 140             iterator = FItems.erase(iterator);
 141             FItems.insert(iterator, item);
 142             break;
 143         }
 144     }
 145 }
 146 
 147 std::list<TString> JavaOptions::ToList() {
 148     std::list<TString> result;
 149     Macros& macros = Macros::GetInstance();
 150 
 151     for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin();
 152         iterator != FItems.end(); iterator++) {
 153         TString key = iterator->name;
 154         TString value = iterator->value;
 155         TString option = Helpers::NameValueToString(key, value);
 156         option = macros.ExpandMacros(option);
 157         result.push_back(option);
 158     }
 159 
 160     return result;
 161 }
 162 
 163 size_t JavaOptions::GetCount() {
 164     return FItems.size();
 165 }
 166 
 167 //----------------------------------------------------------------------------
 168 
 169 JavaVirtualMachine::JavaVirtualMachine() {
 170 }
 171 
 172 JavaVirtualMachine::~JavaVirtualMachine(void) {
 173 }
 174 
 175 bool JavaVirtualMachine::StartJVM() {
 176     Platform& platform = Platform::GetInstance();
 177     Package& package = Package::GetInstance();
 178 
 179     TString classpath = package.GetClassPath();
 180     TString modulepath = package.GetModulePath();
 181     JavaOptions options;
 182 
 183     if (modulepath.empty() == false) {
 184         options.AppendValue(_T("-Djava.module.path"), modulepath);
 185     }
 186 
 187     options.AppendValue(_T("-Djava.library.path"),
 188             package.GetPackageAppDirectory() + FilePath::PathSeparator()
 189             + package.GetPackageLauncherDirectory());
 190     options.AppendValue(
 191             _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory());
 192     options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID());
 193     options.AppendValues(package.GetJVMArgs());
 194 
 195 #ifdef DEBUG
 196     if (package.Debugging() == dsJava) {
 197         options.AppendValue(_T("-Xdebug"), _T(""));
 198         options.AppendValue(
 199                 _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"),
 200                 _T(""));
 201         platform.ShowMessage(_T("localhost:5005"));
 202     }
 203 #endif // DEBUG
 204 
 205     TString maxHeapSizeOption;
 206     TString minHeapSizeOption;
 207 
 208 
 209     if (package.GetMemoryState() == PackageBootFields::msAuto) {
 210         TPlatformNumber memorySize = package.GetMemorySize();
 211         TString memory =
 212                 PlatformString((size_t)memorySize).toString() + _T("m");
 213         maxHeapSizeOption = TString(_T("-Xmx")) + memory;
 214         options.AppendValue(maxHeapSizeOption, _T(""));
 215 
 216         if (memorySize > 256)
 217             minHeapSizeOption = _T("-Xms256m");
 218         else
 219             minHeapSizeOption = _T("-Xms") + memory;
 220 
 221         options.AppendValue(minHeapSizeOption, _T(""));
 222     }
 223 
 224     TString mainClassName = package.GetMainClassName();
 225     TString mainModule = package.GetMainModule();
 226 
 227     if (mainClassName.empty() == true && mainModule.empty() == true) {
 228         Messages& messages = Messages::GetInstance();
 229         platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED));
 230         return false;
 231     }
 232 
 233     configureLibrary();
 234 
 235     // Initialize the arguments to JLI_Launch()
 236     //
 237     // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM.
 238     // This new thread simply re-runs main(argc, argv). Therefore we do not
 239     // want to add new args if we are still in the original main thread so we
 240     // will treat them as command line args provided by the user ...
 241     // Only propagate original set of args first time.
 242 
 243     options.AppendValue(_T("-classpath"));
 244     options.AppendValue(classpath);
 245 
 246     std::list<TString> vmargs;
 247     vmargs.push_back(package.GetCommandName());
 248 
 249     if (package.HasSplashScreen() == true) {
 250         options.AppendValue(TString(_T("-splash:"))
 251                 + package.GetSplashScreenFileName(), _T(""));
 252     }
 253 
 254     if (mainModule.empty() == true) {
 255         options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName),
 256                 _T(""));
 257     } else {
 258         options.AppendValue(_T("-m"));
 259         options.AppendValue(mainModule);
 260     }
 261 
 262     return launchVM(options, vmargs);
 263 }
 264 
 265 void JavaVirtualMachine::configureLibrary() {
 266     Platform& platform = Platform::GetInstance();
 267     Package& package = Package::GetInstance();
 268     // TODO: Clean this up. Because of bug JDK-8131321 the opening of the
 269     // PE file ails in WindowsPlatform.cpp on the check to
 270     // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE)
 271     TString libName = package.GetJVMLibraryFileName();
 272 #ifdef _WIN64
 273     if (FilePath::FileExists(_T("msvcr100.dll")) == true) {
 274         javaLibrary.AddDependency(_T("msvcr100.dll"));
 275     }
 276 
 277     TString runtimeBin = platform.GetPackageRuntimeBinDirectory();
 278     SetDllDirectory(runtimeBin.c_str());
 279 #else
 280     javaLibrary.AddDependencies(
 281             platform.FilterOutRuntimeDependenciesForPlatform(
 282             platform.GetLibraryImports(libName)));
 283 #endif
 284     javaLibrary.Load(libName);
 285 }
 286 
 287 bool JavaVirtualMachine::launchVM(JavaOptions& options,
 288         std::list<TString>& vmargs) {
 289     Platform& platform = Platform::GetInstance();
 290     Package& package = Package::GetInstance();
 291 
 292 #ifdef MAC
 293     // Mac adds a ProcessSerialNumber to args when launched from .app
 294     // filter out the psn since they it's not expected in the app
 295     if (platform.IsMainThread() == false) {
 296         std::list<TString> loptions = options.ToList();
 297         vmargs.splice(vmargs.end(), loptions,
 298                 loptions.begin(), loptions.end());
 299     }
 300 #else
 301     std::list<TString> loptions = options.ToList();
 302     vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end());
 303 #endif
 304 
 305     std::list<TString> largs = package.GetArgs();
 306     vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end());
 307 
 308     size_t argc = vmargs.size();
 309     DynamicBuffer<char*> argv(argc + 1);
 310     if (argv.GetData() == NULL) {
 311         return false;
 312     }
 313 
 314     unsigned int index = 0;
 315     for (std::list<TString>::const_iterator iterator = vmargs.begin();
 316         iterator != vmargs.end(); iterator++) {
 317         TString item = *iterator;
 318         std::string arg = PlatformString(item).toStdString();
 319 #ifdef DEBUG
 320         printf("%i %s\n", index, arg.c_str());
 321 #endif // DEBUG
 322         argv[index] = PlatformString::duplicate(arg.c_str());
 323         index++;
 324     }
 325 
 326     argv[argc] = NULL;
 327 
 328 // On Mac we can only free the boot fields if the calling thread is
 329 // not the main thread.
 330 #ifdef MAC
 331     if (platform.IsMainThread() == false) {
 332         package.FreeBootFields();
 333     }
 334 #else
 335     package.FreeBootFields();
 336 #endif // MAC
 337 
 338     if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) {
 339         return true;
 340     }
 341 
 342     for (index = 0; index < argc; index++) {
 343         if (argv[index] != NULL) {
 344             delete[] argv[index];
 345         }
 346     }
 347 
 348     return false;
 349 }