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 }