| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifdef _MSC_VER | 5 #ifdef _MSC_VER |
| 6 // Do not warn about use of std::copy with raw pointers. | 6 // Do not warn about use of std::copy with raw pointers. |
| 7 #pragma warning(disable : 4996) | 7 #pragma warning(disable : 4996) |
| 8 #endif | 8 #endif |
| 9 | 9 |
| 10 #include "ppapi/native_client/src/trusted/plugin/plugin.h" | 10 #include "ppapi/native_client/src/trusted/plugin/plugin.h" |
| (...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 HistogramEnumerate(name, sample, 7, 6); | 276 HistogramEnumerate(name, sample, 7, 6); |
| 277 } | 277 } |
| 278 | 278 |
| 279 bool Plugin::LoadNaClModuleFromBackgroundThread( | 279 bool Plugin::LoadNaClModuleFromBackgroundThread( |
| 280 nacl::DescWrapper* wrapper, | 280 nacl::DescWrapper* wrapper, |
| 281 NaClSubprocess* subprocess, | 281 NaClSubprocess* subprocess, |
| 282 const Manifest* manifest, | 282 const Manifest* manifest, |
| 283 const SelLdrStartParams& params) { | 283 const SelLdrStartParams& params) { |
| 284 CHECK(!pp::Module::Get()->core()->IsMainThread()); | 284 CHECK(!pp::Module::Get()->core()->IsMainThread()); |
| 285 ServiceRuntime* service_runtime = | 285 ServiceRuntime* service_runtime = |
| 286 new ServiceRuntime(this, manifest, false, | 286 new ServiceRuntime(this, manifest, false, nonsfi_enabled(), |
| 287 pp::BlockUntilComplete(), pp::BlockUntilComplete()); | 287 pp::BlockUntilComplete(), pp::BlockUntilComplete()); |
| 288 subprocess->set_service_runtime(service_runtime); | 288 subprocess->set_service_runtime(service_runtime); |
| 289 PLUGIN_PRINTF(("Plugin::LoadNaClModuleFromBackgroundThread " | 289 PLUGIN_PRINTF(("Plugin::LoadNaClModuleFromBackgroundThread " |
| 290 "(service_runtime=%p)\n", | 290 "(service_runtime=%p)\n", |
| 291 static_cast<void*>(service_runtime))); | 291 static_cast<void*>(service_runtime))); |
| 292 | 292 |
| 293 // Now start the SelLdr instance. This must be created on the main thread. | 293 // Now start the SelLdr instance. This must be created on the main thread. |
| 294 bool service_runtime_started; | 294 bool service_runtime_started; |
| 295 pp::CompletionCallback sel_ldr_callback = | 295 pp::CompletionCallback sel_ldr_callback = |
| 296 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone, | 296 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone, |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 bool* started, | 335 bool* started, |
| 336 ServiceRuntime* service_runtime) { | 336 ServiceRuntime* service_runtime) { |
| 337 *started = (pp_error == PP_OK); | 337 *started = (pp_error == PP_OK); |
| 338 service_runtime->SignalStartSelLdrDone(); | 338 service_runtime->SignalStartSelLdrDone(); |
| 339 } | 339 } |
| 340 | 340 |
| 341 void Plugin::LoadNaClModule(nacl::DescWrapper* wrapper, | 341 void Plugin::LoadNaClModule(nacl::DescWrapper* wrapper, |
| 342 bool enable_dyncode_syscalls, | 342 bool enable_dyncode_syscalls, |
| 343 bool enable_exception_handling, | 343 bool enable_exception_handling, |
| 344 bool enable_crash_throttling, | 344 bool enable_crash_throttling, |
| 345 bool enable_nonsfi, |
| 345 const pp::CompletionCallback& init_done_cb, | 346 const pp::CompletionCallback& init_done_cb, |
| 346 const pp::CompletionCallback& crash_cb) { | 347 const pp::CompletionCallback& crash_cb) { |
| 347 nacl::scoped_ptr<nacl::DescWrapper> scoped_wrapper(wrapper); | 348 nacl::scoped_ptr<nacl::DescWrapper> scoped_wrapper(wrapper); |
| 348 CHECK(pp::Module::Get()->core()->IsMainThread()); | 349 CHECK(pp::Module::Get()->core()->IsMainThread()); |
| 349 // Before forking a new sel_ldr process, ensure that we do not leak | 350 // Before forking a new sel_ldr process, ensure that we do not leak |
| 350 // the ServiceRuntime object for an existing subprocess, and that any | 351 // the ServiceRuntime object for an existing subprocess, and that any |
| 351 // associated listener threads do not go unjoined because if they | 352 // associated listener threads do not go unjoined because if they |
| 352 // outlive the Plugin object, they will not be memory safe. | 353 // outlive the Plugin object, they will not be memory safe. |
| 353 ShutDownSubprocesses(); | 354 ShutDownSubprocesses(); |
| 354 SelLdrStartParams params(manifest_base_url(), | 355 SelLdrStartParams params(manifest_base_url(), |
| 355 true /* uses_irt */, | 356 true /* uses_irt */, |
| 356 true /* uses_ppapi */, | 357 true /* uses_ppapi */, |
| 357 enable_dev_interfaces_, | 358 enable_dev_interfaces_, |
| 358 enable_dyncode_syscalls, | 359 enable_dyncode_syscalls, |
| 359 enable_exception_handling, | 360 enable_exception_handling, |
| 360 enable_crash_throttling); | 361 enable_crash_throttling, |
| 362 enable_nonsfi); |
| 361 ErrorInfo error_info; | 363 ErrorInfo error_info; |
| 362 ServiceRuntime* service_runtime = | 364 ServiceRuntime* service_runtime = |
| 363 new ServiceRuntime(this, manifest_.get(), true, init_done_cb, crash_cb); | 365 new ServiceRuntime(this, manifest_.get(), true, nonsfi_enabled(), |
| 366 init_done_cb, crash_cb); |
| 364 main_subprocess_.set_service_runtime(service_runtime); | 367 main_subprocess_.set_service_runtime(service_runtime); |
| 365 PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n", | 368 PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n", |
| 366 static_cast<void*>(service_runtime))); | 369 static_cast<void*>(service_runtime))); |
| 367 if (NULL == service_runtime) { | 370 if (NULL == service_runtime) { |
| 368 error_info.SetReport( | 371 error_info.SetReport( |
| 369 PP_NACL_ERROR_SEL_LDR_INIT, | 372 PP_NACL_ERROR_SEL_LDR_INIT, |
| 370 "sel_ldr init failure " + main_subprocess_.description()); | 373 "sel_ldr init failure " + main_subprocess_.description()); |
| 371 ReportLoadError(error_info); | 374 ReportLoadError(error_info); |
| 372 return; | 375 return; |
| 373 } | 376 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 391 bool nexe_loaded = service_runtime->LoadNexeAndStart(wrapper, crash_cb); | 394 bool nexe_loaded = service_runtime->LoadNexeAndStart(wrapper, crash_cb); |
| 392 PLUGIN_PRINTF(("Plugin::LoadNaClModule (nexe_loaded=%d)\n", | 395 PLUGIN_PRINTF(("Plugin::LoadNaClModule (nexe_loaded=%d)\n", |
| 393 nexe_loaded)); | 396 nexe_loaded)); |
| 394 if (nexe_loaded) { | 397 if (nexe_loaded) { |
| 395 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n", | 398 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n", |
| 396 main_subprocess_.detailed_description().c_str())); | 399 main_subprocess_.detailed_description().c_str())); |
| 397 } | 400 } |
| 398 } | 401 } |
| 399 | 402 |
| 400 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) { | 403 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) { |
| 401 if (!main_subprocess_.StartSrpcServices()) { | 404 if (!nonsfi_enabled()) { |
| 402 // The NaCl process probably crashed. On Linux, a crash causes this error, | 405 if (!main_subprocess_.StartSrpcServices()) { |
| 403 // while on other platforms, the error is detected below, when we attempt to | 406 // The NaCl process probably crashed. On Linux, a crash causes this |
| 404 // start the proxy. Report a module initialization error here, to make it | 407 // error, while on other platforms, the error is detected below, when we |
| 405 // less confusing for developers. | 408 // attempt to start the proxy. Report a module initialization error here, |
| 406 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: " | 409 // to make it less confusing for developers. |
| 407 "StartSrpcServices failed\n"); | 410 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: " |
| 408 error_info->SetReport(PP_NACL_ERROR_START_PROXY_MODULE, | 411 "StartSrpcServices failed\n"); |
| 409 "could not initialize module."); | 412 error_info->SetReport(PP_NACL_ERROR_START_PROXY_MODULE, |
| 410 return false; | 413 "could not initialize module."); |
| 414 return false; |
| 415 } |
| 411 } | 416 } |
| 412 PP_ExternalPluginResult ipc_result = | 417 PP_ExternalPluginResult ipc_result = |
| 413 nacl_interface_->StartPpapiProxy(pp_instance()); | 418 nacl_interface_->StartPpapiProxy(pp_instance()); |
| 414 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) { | 419 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) { |
| 415 // Log the amound of time that has passed between the trusted plugin being | 420 // Log the amound of time that has passed between the trusted plugin being |
| 416 // initialized and the untrusted plugin being initialized. This is | 421 // initialized and the untrusted plugin being initialized. This is |
| 417 // (roughly) the cost of using NaCl, in terms of startup time. | 422 // (roughly) the cost of using NaCl, in terms of startup time. |
| 418 HistogramStartupTimeMedium( | 423 HistogramStartupTimeMedium( |
| 419 "NaCl.Perf.StartupTime.NaClOverhead", | 424 "NaCl.Perf.StartupTime.NaClOverhead", |
| 420 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_) | 425 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_) |
| (...skipping 30 matching lines...) Expand all Loading... |
| 451 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is | 456 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is |
| 452 // done to save on address space and swap space. | 457 // done to save on address space and swap space. |
| 453 // TODO(jvoung): See if we still need the uses_ppapi variable, now that | 458 // TODO(jvoung): See if we still need the uses_ppapi variable, now that |
| 454 // LaunchSelLdr always happens on the main thread. | 459 // LaunchSelLdr always happens on the main thread. |
| 455 SelLdrStartParams params(manifest_base_url(), | 460 SelLdrStartParams params(manifest_base_url(), |
| 456 false /* uses_irt */, | 461 false /* uses_irt */, |
| 457 false /* uses_ppapi */, | 462 false /* uses_ppapi */, |
| 458 enable_dev_interfaces_, | 463 enable_dev_interfaces_, |
| 459 false /* enable_dyncode_syscalls */, | 464 false /* enable_dyncode_syscalls */, |
| 460 false /* enable_exception_handling */, | 465 false /* enable_exception_handling */, |
| 461 true /* enable_crash_throttling */); | 466 true /* enable_crash_throttling */, |
| 467 false /* enable_nonsfi */); |
| 462 if (!LoadNaClModuleFromBackgroundThread(wrapper, nacl_subprocess.get(), | 468 if (!LoadNaClModuleFromBackgroundThread(wrapper, nacl_subprocess.get(), |
| 463 manifest, params)) { | 469 manifest, params)) { |
| 464 return NULL; | 470 return NULL; |
| 465 } | 471 } |
| 466 // We need not wait for the init_done callback. We can block | 472 // We need not wait for the init_done callback. We can block |
| 467 // here in StartSrpcServices, since helper NaCl modules | 473 // here in StartSrpcServices, since helper NaCl modules |
| 468 // are spawned from a private thread. | 474 // are spawned from a private thread. |
| 469 // | 475 // |
| 470 // TODO(bsy): if helper module crashes, we should abort. | 476 // TODO(bsy): if helper module crashes, we should abort. |
| 471 // crash_cb is not used here, so we are relying on crashes | 477 // crash_cb is not used here, so we are relying on crashes |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 756 | 762 |
| 757 load_start_ = NaClGetTimeOfDayMicroseconds(); | 763 load_start_ = NaClGetTimeOfDayMicroseconds(); |
| 758 nacl::scoped_ptr<nacl::DescWrapper> | 764 nacl::scoped_ptr<nacl::DescWrapper> |
| 759 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY)); | 765 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY)); |
| 760 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n"); | 766 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n"); |
| 761 LoadNaClModule( | 767 LoadNaClModule( |
| 762 wrapper.release(), | 768 wrapper.release(), |
| 763 true, /* enable_dyncode_syscalls */ | 769 true, /* enable_dyncode_syscalls */ |
| 764 true, /* enable_exception_handling */ | 770 true, /* enable_exception_handling */ |
| 765 false, /* enable_crash_throttling */ | 771 false, /* enable_crash_throttling */ |
| 772 nonsfi_enabled(), |
| 766 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation), | 773 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation), |
| 767 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); | 774 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); |
| 768 } | 775 } |
| 769 | 776 |
| 770 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) { | 777 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) { |
| 771 ErrorInfo error_info; | 778 ErrorInfo error_info; |
| 772 bool was_successful; | 779 bool was_successful; |
| 773 | 780 |
| 774 UNREFERENCED_PARAMETER(pp_error); | 781 UNREFERENCED_PARAMETER(pp_error); |
| 775 NaClLog(4, "Entered NexeFileDidOpenContinuation\n"); | 782 NaClLog(4, "Entered NexeFileDidOpenContinuation\n"); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 } | 880 } |
| 874 | 881 |
| 875 // Inform JavaScript that we successfully translated the bitcode to a nexe. | 882 // Inform JavaScript that we successfully translated the bitcode to a nexe. |
| 876 nacl::scoped_ptr<nacl::DescWrapper> | 883 nacl::scoped_ptr<nacl::DescWrapper> |
| 877 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD()); | 884 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD()); |
| 878 LoadNaClModule( | 885 LoadNaClModule( |
| 879 wrapper.release(), | 886 wrapper.release(), |
| 880 false, /* enable_dyncode_syscalls */ | 887 false, /* enable_dyncode_syscalls */ |
| 881 false, /* enable_exception_handling */ | 888 false, /* enable_exception_handling */ |
| 882 true, /* enable_crash_throttling */ | 889 true, /* enable_crash_throttling */ |
| 890 false, /* enable_nonsfi */ |
| 883 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation), | 891 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation), |
| 884 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); | 892 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); |
| 885 } | 893 } |
| 886 | 894 |
| 887 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) { | 895 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) { |
| 888 ErrorInfo error_info; | 896 ErrorInfo error_info; |
| 889 bool was_successful = LoadNaClModuleContinuationIntern(&error_info); | 897 bool was_successful = LoadNaClModuleContinuationIntern(&error_info); |
| 890 | 898 |
| 891 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n"); | 899 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n"); |
| 892 UNREFERENCED_PARAMETER(pp_error); | 900 UNREFERENCED_PARAMETER(pp_error); |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1027 ReportLoadError(error_info); | 1035 ReportLoadError(error_info); |
| 1028 return; | 1036 return; |
| 1029 } | 1037 } |
| 1030 | 1038 |
| 1031 ProcessNaClManifest(json_buffer); | 1039 ProcessNaClManifest(json_buffer); |
| 1032 } | 1040 } |
| 1033 | 1041 |
| 1034 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) { | 1042 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) { |
| 1035 HistogramSizeKB("NaCl.Perf.Size.Manifest", | 1043 HistogramSizeKB("NaCl.Perf.Size.Manifest", |
| 1036 static_cast<int32_t>(manifest_json.length() / 1024)); | 1044 static_cast<int32_t>(manifest_json.length() / 1024)); |
| 1037 nacl::string program_url; | |
| 1038 PnaclOptions pnacl_options; | |
| 1039 ErrorInfo error_info; | 1045 ErrorInfo error_info; |
| 1040 if (!SetManifestObject(manifest_json, &error_info)) { | 1046 if (!SetManifestObject(manifest_json, &error_info)) { |
| 1041 ReportLoadError(error_info); | 1047 ReportLoadError(error_info); |
| 1042 return; | 1048 return; |
| 1043 } | 1049 } |
| 1044 | 1050 |
| 1045 if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) { | 1051 nacl::string program_url; |
| 1052 PnaclOptions pnacl_options; |
| 1053 bool nonsfi_enabled; |
| 1054 if (manifest_->GetProgramURL( |
| 1055 &program_url, &pnacl_options, &nonsfi_enabled, &error_info)) { |
| 1046 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION; | 1056 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION; |
| 1057 set_nonsfi_enabled(nonsfi_enabled); |
| 1047 set_nacl_ready_state(LOADING); | 1058 set_nacl_ready_state(LOADING); |
| 1048 // Inform JavaScript that we found a nexe URL to load. | 1059 // Inform JavaScript that we found a nexe URL to load. |
| 1049 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS); | 1060 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS); |
| 1050 if (pnacl_options.translate()) { | 1061 if (pnacl_options.translate()) { |
| 1051 pp::CompletionCallback translate_callback = | 1062 pp::CompletionCallback translate_callback = |
| 1052 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate); | 1063 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate); |
| 1053 // Will always call the callback on success or failure. | 1064 // Will always call the callback on success or failure. |
| 1054 pnacl_coordinator_.reset( | 1065 pnacl_coordinator_.reset( |
| 1055 PnaclCoordinator::BitcodeToNative(this, | 1066 PnaclCoordinator::BitcodeToNative(this, |
| 1056 program_url, | 1067 program_url, |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1130 | 1141 |
| 1131 bool Plugin::SetManifestObject(const nacl::string& manifest_json, | 1142 bool Plugin::SetManifestObject(const nacl::string& manifest_json, |
| 1132 ErrorInfo* error_info) { | 1143 ErrorInfo* error_info) { |
| 1133 PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n", | 1144 PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n", |
| 1134 manifest_json.c_str())); | 1145 manifest_json.c_str())); |
| 1135 if (error_info == NULL) | 1146 if (error_info == NULL) |
| 1136 return false; | 1147 return false; |
| 1137 // Determine whether lookups should use portable (i.e., pnacl versions) | 1148 // Determine whether lookups should use portable (i.e., pnacl versions) |
| 1138 // rather than platform-specific files. | 1149 // rather than platform-specific files. |
| 1139 bool is_pnacl = (mime_type() == kPnaclMIMEType); | 1150 bool is_pnacl = (mime_type() == kPnaclMIMEType); |
| 1151 bool nonsfi_enabled = PP_ToBool(nacl_interface_->IsNonSFIEnabled()); |
| 1140 nacl::scoped_ptr<JsonManifest> json_manifest( | 1152 nacl::scoped_ptr<JsonManifest> json_manifest( |
| 1141 new JsonManifest(url_util_, | 1153 new JsonManifest(url_util_, |
| 1142 manifest_base_url(), | 1154 manifest_base_url(), |
| 1143 (is_pnacl ? kPortableISA : GetSandboxISA()))); | 1155 (is_pnacl ? kPortableISA : GetSandboxISA()), |
| 1156 nonsfi_enabled)); |
| 1144 if (!json_manifest->Init(manifest_json, error_info)) { | 1157 if (!json_manifest->Init(manifest_json, error_info)) { |
| 1145 return false; | 1158 return false; |
| 1146 } | 1159 } |
| 1147 manifest_.reset(json_manifest.release()); | 1160 manifest_.reset(json_manifest.release()); |
| 1148 return true; | 1161 return true; |
| 1149 } | 1162 } |
| 1150 | 1163 |
| 1151 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error, | 1164 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error, |
| 1152 FileDownloader*& url_downloader, | 1165 FileDownloader*& url_downloader, |
| 1153 PP_CompletionCallback callback) { | 1166 PP_CompletionCallback callback) { |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1477 DCHECK(pp::Module::Get()->core()->IsMainThread()); | 1490 DCHECK(pp::Module::Get()->core()->IsMainThread()); |
| 1478 DCHECK(nacl_interface_); | 1491 DCHECK(nacl_interface_); |
| 1479 exit_status_ = exit_status; | 1492 exit_status_ = exit_status; |
| 1480 nacl_interface_->SetReadOnlyProperty(pp_instance(), | 1493 nacl_interface_->SetReadOnlyProperty(pp_instance(), |
| 1481 pp::Var("exitStatus").pp_var(), | 1494 pp::Var("exitStatus").pp_var(), |
| 1482 pp::Var(exit_status_).pp_var()); | 1495 pp::Var(exit_status_).pp_var()); |
| 1483 } | 1496 } |
| 1484 | 1497 |
| 1485 | 1498 |
| 1486 } // namespace plugin | 1499 } // namespace plugin |
| OLD | NEW |