modules/jdk.packager/src/main/native/library/common/JavaVirtualMachine.cpp

Print this page




  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         javavm.ShutdownJVM();
  58     }
  59     else {
  60         Platform& platform = Platform::GetInstance();
  61         platform.ShowMessage(_T("Failed to launch JVM\n"));
  62     }
  63 
  64     return result;
  65 }
  66 
  67 
  68 // Private typedef for function pointer casting
  69 #ifndef USE_JLI_LAUNCH
  70 #define LAUNCH_FUNC "JNI_CreateJavaVM"
  71 typedef jint (JNICALL *JVM_CREATE)(JavaVM ** jvm, JNIEnv ** env, void *);
  72 #else
  73 #define LAUNCH_FUNC "JLI_Launch"
  74 typedef int (JNICALL *JVM_CREATE)(int argc, char ** argv,
  75                                     int jargc, const char** jargv,
  76                                     int appclassc, const char** appclassv,
  77                                     const char* fullversion,
  78                                     const char* dotversion,
  79                                     const char* pname,
  80                                     const char* lname,
  81                                     jboolean javaargs,
  82                                     jboolean cpwildcard,
  83                                     jboolean javaw,
  84                                     jint ergo);
  85 #endif //USE_JLI_LAUNCH
  86 
  87 class JavaLibrary : public Library {
  88     JVM_CREATE FCreateProc;
  89 
  90     JavaLibrary(const TString &FileName);
  91 
  92 public:
  93     JavaLibrary() : Library() {
  94         FCreateProc = NULL;
  95     }
  96 
  97 #ifndef USE_JLI_LAUNCH
  98 bool JavaVMCreate(JavaVM** jvm, JNIEnv** env, void* jvmArgs) {
  99         bool result = true;
 100 
 101         if (FCreateProc == NULL) {
 102             FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC);
 103         }
 104 
 105         if (FCreateProc == NULL) {
 106             Platform& platform = Platform::GetInstance();
 107             Messages& messages = Messages::GetInstance();
 108             platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT));
 109             return false;
 110         }
 111 
 112         if ((*FCreateProc)(jvm, env, jvmArgs) < 0) {
 113             Platform& platform = Platform::GetInstance();
 114             Messages& messages = Messages::GetInstance();
 115             platform.ShowMessage(messages.GetMessage(FAILED_CREATING_JVM));
 116             return false;
 117         }
 118 
 119         return result;
 120     }
 121 #else
 122     bool JavaVMCreate(size_t argc, char *argv[]) {
 123         if (FCreateProc == NULL) {
 124             FCreateProc = (JVM_CREATE)GetProcAddress(LAUNCH_FUNC);
 125         }
 126 
 127         if (FCreateProc == NULL) {
 128             Platform& platform = Platform::GetInstance();
 129             Messages& messages = Messages::GetInstance();
 130             platform.ShowMessage(messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT));
 131             return false;
 132         }
 133 
 134         return FCreateProc((int)argc, argv,
 135             0, NULL,
 136             0, NULL,
 137             "",
 138             "",
 139             "java",
 140             "java",
 141             false,
 142             false,
 143             false,
 144             0) == 0;
 145     }
 146 #endif //USE_JLI_LAUNCH
 147 };
 148 
 149 #ifndef USE_JLI_LAUNCH
 150 //debug hook to print JVM messages into console.
 151 static jint JNICALL vfprintfHook(FILE *fp, const char *format, va_list args) {
 152 #ifdef WINDOWS
 153    char buffer[20480];
 154    int len;
 155    HANDLE hConsole;
 156    DWORD wasWritten;
 157 
 158    len = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), format, args);
 159 
 160    if (len <= 0) {
 161         return len;
 162    }
 163 
 164    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 165 
 166    if (hConsole == INVALID_HANDLE_VALUE) {
 167         return false;
 168    }
 169 
 170    //JVM will always pass us ASCII
 171    WriteConsoleA(hConsole, buffer, strlen(buffer), &wasWritten, NULL);
 172 
 173    return (jint) len;
 174 #endif //WINDOWS
 175 #ifdef LINUX
 176    return 0;
 177 #endif //LINUX
 178 }
 179 #endif //USE_JLI_LAUNCH
 180 
 181 //--------------------------------------------------------------------------------------------------
 182 
 183 struct JavaOptionItem {
 184     TString name;
 185     TString value;
 186     void* extraInfo;
 187 };
 188 
 189 
 190 class JavaOptions {
 191 private:
 192     std::list<JavaOptionItem> FItems;
 193     JavaVMOption* FOptions;
 194 
 195 public:
 196     JavaOptions() {
 197         FOptions = NULL;
 198 
 199 #ifndef USE_JLI_LAUNCH
 200 #ifdef DEBUG
 201         Platform& platform = Platform::GetInstance();
 202 
 203         if (platform.GetDebugState() == dsNative) {
 204             AppendValue(_T("vfprintf"), _T(""), (void*)vfprintfHook);
 205         }
 206 #endif //DEBUG
 207 #endif //USE_JLI_LAUNCH
 208     }
 209 
 210     ~JavaOptions() {
 211         if (FOptions != NULL) {
 212             for (unsigned int index = 0; index < GetCount(); index++) {
 213                 delete[] FOptions[index].optionString;
 214             }
 215 
 216             delete[] FOptions;
 217         }
 218     }
 219 
 220     void AppendValue(const TString Key, TString Value, void* Extra) {
 221         JavaOptionItem item;
 222         item.name = Key;
 223         item.value = Value;
 224         item.extraInfo = Extra;
 225         FItems.push_back(item);
 226     }
 227 


 245             }
 246         }
 247     }
 248 
 249     void ReplaceValue(const TString Key, TString Value) {
 250         for (std::list<JavaOptionItem>::iterator iterator = FItems.begin();
 251              iterator != FItems.end(); iterator++) {
 252 
 253             TString lkey = iterator->name;
 254 
 255             if (lkey == Key) {
 256                 JavaOptionItem item = *iterator;
 257                 item.value = Value;
 258                 iterator = FItems.erase(iterator);
 259                 FItems.insert(iterator, item);
 260                 break;
 261             }
 262         }
 263     }
 264 
 265 #ifndef USE_JLI_LAUNCH
 266     JavaVMOption* ToJavaOptions() {
 267         FOptions = new JavaVMOption[FItems.size()];
 268         memset(FOptions, 0, sizeof(JavaVMOption) * FItems.size());
 269         Macros& macros = Macros::GetInstance();
 270         unsigned int index = 0;
 271 
 272         for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin();
 273              iterator != FItems.end(); iterator++) {
 274             TString key = iterator->name;
 275             TString value = iterator->value;
 276             TString option = Helpers::NameValueToString(key, value);
 277             option = macros.ExpandMacros(option);
 278 #ifdef DEBUG
 279             printf("%s\n", PlatformString(option).c_str());
 280 #endif //DEBUG
 281             FOptions[index].optionString = PlatformString::duplicate(PlatformString(option).c_str());
 282             FOptions[index].extraInfo = iterator->extraInfo;
 283             index++;
 284         }
 285 
 286         return FOptions;
 287     }
 288 #else
 289     std::list<TString> ToList() {
 290         std::list<TString> result;
 291         Macros& macros = Macros::GetInstance();
 292 
 293         for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin();
 294              iterator != FItems.end(); iterator++) {
 295             TString key = iterator->name;
 296             TString value = iterator->value;
 297             TString option = Helpers::NameValueToString(key, value);
 298             option = macros.ExpandMacros(option);
 299             result.push_back(option);
 300         }
 301 
 302         return result;
 303     }
 304 #endif //USE_JLI_LAUNCH
 305 
 306     size_t GetCount() {
 307         return FItems.size();
 308     }
 309 };
 310 
 311 // jvmuserargs can have a trailing equals in the key. This needs to be removed to use
 312 // other parts of the launcher.
 313 OrderedMap<TString, TString> RemoveTrailingEquals(OrderedMap<TString, TString> Map) {
 314     OrderedMap<TString, TString> result;
 315 
 316     std::vector<TString> keys = Map.GetKeys();
 317 
 318     for (size_t index = 0; index < keys.size(); index++) {
 319         TString name = keys[index];
 320         TString value;
 321 
 322         if (Map.GetValue(name, value) == true) {
 323             // If the last character of the key is an equals, then remove it. If there is no
 324             // equals then combine the two as a key.


 333 
 334                 if (*i == '=') {
 335                     value = value.substr(1, value.size() - 1);
 336                 }
 337                 else {
 338                     name = name + value;
 339                     value = _T("");
 340                 }
 341             }
 342 
 343             result.Append(name, value);
 344         }
 345     }
 346 
 347     return result;
 348 }
 349 
 350 //--------------------------------------------------------------------------------------------------
 351 
 352 JavaVirtualMachine::JavaVirtualMachine() {
 353 #ifndef USE_JLI_LAUNCH
 354     FEnv = NULL;
 355     FJvm = NULL;
 356 #endif //USE_JLI_LAUNCH
 357 }
 358 
 359 JavaVirtualMachine::~JavaVirtualMachine(void) {
 360 }
 361 
 362 bool JavaVirtualMachine::StartJVM() {
 363     Platform& platform = Platform::GetInstance();
 364     Package& package = Package::GetInstance();
 365 
 366     TString classpath = package.GetClassPath();
 367     TString modulepath = package.GetModulePath();
 368     JavaOptions options;
 369 
 370     if (modulepath.empty() == false) {
 371         options.AppendValue(_T("-Djava.module.path"), modulepath);
 372     }
 373 
 374     options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory());
 375     options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory());
 376     options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID());


 409     if (mainClassName.empty() == true && mainModule.empty() == true) {
 410         Messages& messages = Messages::GetInstance();
 411         platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED));
 412         return false;
 413     }
 414 
 415     JavaLibrary javaLibrary;
 416 
 417     // TODO: Clean this up. Because of bug JDK-8131321 the opening of the PE file fails in WindowsPlatform.cpp on the check to
 418     // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE)
 419 #ifdef _WIN64
 420     if (FilePath::FileExists(_T("msvcr100.dll")) == true) {
 421         javaLibrary.AddDependency(_T("msvcr100.dll"));
 422     }
 423 #else
 424     javaLibrary.AddDependencies(platform.FilterOutRuntimeDependenciesForPlatform(platform.GetLibraryImports(package.GetJVMLibraryFileName())));
 425 #endif
 426 
 427     javaLibrary.Load(package.GetJVMLibraryFileName());
 428 
 429 #ifndef USE_JLI_LAUNCH
 430     options.AppendValue(_T("-Djava.class.path"), classpath);
 431 
 432     if (package.HasSplashScreen() == true) {
 433         options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T(""));
 434     }
 435 
 436     // Set up the VM init args
 437     JavaVMInitArgs jvmArgs;
 438     memset(&jvmArgs, 0, sizeof(JavaVMInitArgs));
 439     jvmArgs.version = JNI_VERSION_1_6;
 440     jvmArgs.options = options.ToJavaOptions();
 441     jvmArgs.nOptions = (jint)options.GetCount();
 442     jvmArgs.ignoreUnrecognized = JNI_TRUE;
 443 
 444     if (javaLibrary.JavaVMCreate(&FJvm, &FEnv, &jvmArgs) == true) {
 445         try {
 446             JavaClass mainClass(FEnv, Helpers::ConvertIdToJavaPath(mainClassName));
 447             JavaStaticMethod mainMethod = mainClass.GetStaticMethod(_T("main"), _T("([Ljava/lang/String;)V"));
 448             std::list<TString> appargs = package.GetArgs();
 449             JavaStringArray largs(FEnv, appargs);
 450 
 451             package.FreeBootFields();
 452 
 453             mainMethod.CallVoidMethod(1, largs.GetData());
 454             return true;
 455         }
 456         catch (JavaException& exception) {
 457             platform.ShowMessage(exception.GetMessage());
 458             return false;
 459         }
 460     }
 461 
 462     return false;
 463 }
 464 #else
 465     // Initialize the arguments to JLI_Launch()
 466     //
 467     // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM. This
 468     // new thread simply re-runs main(argc, argv). Therefore we do not want
 469     // to add new args if we are still in the original main thread so we
 470     // will treat them as command line args provided by the user ...
 471     // Only propagate original set of args first time.
 472 
 473     options.AppendValue(_T("-classpath"));
 474     options.AppendValue(classpath);
 475 
 476     std::list<TString> vmargs;
 477     vmargs.push_back(package.GetCommandName());
 478 
 479     if (package.HasSplashScreen() == true) {
 480         options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T(""));
 481     }
 482 
 483     if (mainModule.empty() == true) {
 484         options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName), _T(""));


 523     #ifdef MAC
 524     if (platform.IsMainThread() == false) {
 525         package.FreeBootFields();
 526     }
 527     #else
 528     package.FreeBootFields();
 529     #endif //MAC
 530 
 531     if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) {
 532         return true;
 533     }
 534 
 535     for (index = 0; index < argc; index++) {
 536         if (argv[index] != NULL) {
 537             delete[] argv[index];
 538         }
 539     }
 540 
 541     return false;
 542 }
 543 #endif //USE_JLI_LAUNCH
 544 
 545 void JavaVirtualMachine::ShutdownJVM() {
 546 #ifndef USE_JLI_LAUNCH
 547     if (FJvm != NULL) {
 548         // If application main() exits quickly but application is run on some other thread
 549         //  (e.g. Swing app performs invokeLater() in main and exits)
 550         // then if we return execution to tWinMain it will exit.
 551         // This will cause process to exit and application will not actually run.
 552         //
 553         // To avoid this we are trying to detach jvm from current thread (java.exe does the same)
 554         // Because we are doing this on the main JVM thread (i.e. one that was used to create JVM)
 555         // this call will spawn "Destroy Java VM" java thread that will shut JVM once there are
 556         // no non-daemon threads running, and then return control here.
 557         // I.e. this will happen when EDT and other app thread will exit.
 558         if (FJvm->DetachCurrentThread() != JNI_OK) {
 559             Platform& platform = Platform::GetInstance();
 560             platform.ShowMessage(_T("Detach failed."));
 561         }
 562 
 563         FJvm->DestroyJavaVM();
 564     }
 565 #endif //USE_JLI_LAUNCH
 566 }


  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 


 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.


 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());


 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(""));


 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 }