Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" | 7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" |
| 8 | 8 |
| 9 #include "native_client/src/trusted/plugin/service_runtime.h" | 9 #include "native_client/src/trusted/plugin/service_runtime.h" |
| 10 | 10 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 39 // PostMessage method, so this undef must appear before any of those. | 39 // PostMessage method, so this undef must appear before any of those. |
| 40 #ifdef PostMessage | 40 #ifdef PostMessage |
| 41 #undef PostMessage | 41 #undef PostMessage |
| 42 #endif | 42 #endif |
| 43 #include "native_client/src/trusted/plugin/plugin.h" | 43 #include "native_client/src/trusted/plugin/plugin.h" |
| 44 #include "native_client/src/trusted/plugin/plugin_error.h" | 44 #include "native_client/src/trusted/plugin/plugin_error.h" |
| 45 #include "native_client/src/trusted/plugin/pnacl_coordinator.h" | 45 #include "native_client/src/trusted/plugin/pnacl_coordinator.h" |
| 46 #include "native_client/src/trusted/plugin/pnacl_resources.h" | 46 #include "native_client/src/trusted/plugin/pnacl_resources.h" |
| 47 #include "native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" | 47 #include "native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" |
| 48 #include "native_client/src/trusted/plugin/srpc_client.h" | 48 #include "native_client/src/trusted/plugin/srpc_client.h" |
| 49 | 49 #include "native_client/src/trusted/reverse_service/nacl_file_info.h" |
| 50 #include "native_client/src/trusted/weak_ref/call_on_main_thread.h" | |
| 51 | |
| 52 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" | 50 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" |
| 53 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" | 51 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" |
| 52 #include "native_client/src/trusted/weak_ref/call_on_main_thread.h" | |
| 54 | 53 |
| 55 #include "ppapi/c/pp_errors.h" | 54 #include "ppapi/c/pp_errors.h" |
| 56 #include "ppapi/c/trusted/ppb_file_io_trusted.h" | 55 #include "ppapi/c/trusted/ppb_file_io_trusted.h" |
| 57 #include "ppapi/cpp/core.h" | 56 #include "ppapi/cpp/core.h" |
| 58 #include "ppapi/cpp/completion_callback.h" | 57 #include "ppapi/cpp/completion_callback.h" |
| 59 #include "ppapi/cpp/file_io.h" | 58 #include "ppapi/cpp/file_io.h" |
| 60 | 59 |
| 61 namespace { | 60 namespace { |
| 62 | 61 |
| 63 // For doing crude quota enforcement on writes to temp files. | 62 // For doing crude quota enforcement on writes to temp files. |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 return false; | 164 return false; |
| 166 } | 165 } |
| 167 | 166 |
| 168 return true; | 167 return true; |
| 169 } | 168 } |
| 170 | 169 |
| 171 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey | 170 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey |
| 172 // and invoke StreamAsFile with a completion callback that invokes | 171 // and invoke StreamAsFile with a completion callback that invokes |
| 173 // GetPOSIXFileDesc. | 172 // GetPOSIXFileDesc. |
| 174 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, | 173 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, |
| 175 int32_t* out_desc) { | 174 struct NaClFileInfo *info) { |
| 176 ErrorInfo error_info; | 175 ErrorInfo error_info; |
| 177 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! | 176 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! |
| 178 OpenManifestEntryResource* to_open = | 177 OpenManifestEntryResource* to_open = |
| 179 new OpenManifestEntryResource(url_key, out_desc, | 178 new OpenManifestEntryResource(url_key, info, |
| 180 &error_info, &op_complete); | 179 &error_info, &op_complete); |
|
dmichael (off chromium)
2013/05/15 18:37:53
I know this isn't your fault, but this seems to wa
dmichael (off chromium)
2013/05/16 17:08:35
Could you comment this a little, pretty please? Th
Nick Bray (chromium)
2013/05/16 17:44:15
Done.
| |
| 181 CHECK(to_open != NULL); | 180 CHECK(to_open != NULL); |
| 182 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", | 181 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", |
| 183 url_key.c_str()); | 182 url_key.c_str()); |
| 184 // This assumes we are not on the main thread. If false, we deadlock. | 183 // This assumes we are not on the main thread. If false, we deadlock. |
| 185 plugin::WeakRefCallOnMainThread( | 184 plugin::WeakRefCallOnMainThread( |
| 186 anchor_, | 185 anchor_, |
| 187 0, | 186 0, |
| 188 this, | 187 this, |
| 189 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, | 188 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, |
| 190 to_open); | 189 to_open); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 221 // The caller is responsible for not closing *out_desc. If it is | 220 // The caller is responsible for not closing *out_desc. If it is |
| 222 // closed prematurely, then another open could re-use the OS | 221 // closed prematurely, then another open could re-use the OS |
| 223 // descriptor, confusing the opened_ map. If the caller is going to | 222 // descriptor, confusing the opened_ map. If the caller is going to |
| 224 // want to make a NaClDesc object and transfer it etc., then the | 223 // want to make a NaClDesc object and transfer it etc., then the |
| 225 // caller should DUP the descriptor (but remember the original | 224 // caller should DUP the descriptor (but remember the original |
| 226 // value) for use by the NaClDesc object, which closes when the | 225 // value) for use by the NaClDesc object, which closes when the |
| 227 // object is destroyed. | 226 // object is destroyed. |
| 228 NaClLog(4, | 227 NaClLog(4, |
| 229 "PluginReverseInterface::OpenManifestEntry:" | 228 "PluginReverseInterface::OpenManifestEntry:" |
| 230 " *out_desc = %d\n", | 229 " *out_desc = %d\n", |
| 231 *out_desc); | 230 info->desc); |
| 232 if (*out_desc == -1) { | 231 if (info->desc == -1) { |
| 233 // TODO(bsy,ncbray): what else should we do with the error? This | 232 // TODO(bsy,ncbray): what else should we do with the error? This |
| 234 // is a runtime error that may simply be a programming error in | 233 // is a runtime error that may simply be a programming error in |
| 235 // the untrusted code, or it may be something else wrong w/ the | 234 // the untrusted code, or it may be something else wrong w/ the |
| 236 // manifest. | 235 // manifest. |
| 237 NaClLog(4, | 236 NaClLog(4, |
| 238 "OpenManifestEntry: failed for key %s, code %d (%s)\n", | 237 "OpenManifestEntry: failed for key %s, code %d (%s)\n", |
| 239 url_key.c_str(), | 238 url_key.c_str(), |
| 240 error_info.error_code(), | 239 error_info.error_code(), |
| 241 error_info.message().c_str()); | 240 error_info.message().c_str()); |
| 242 } | 241 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 260 | 259 |
| 261 std::string mapped_url; | 260 std::string mapped_url; |
| 262 PnaclOptions pnacl_options; | 261 PnaclOptions pnacl_options; |
| 263 if (!manifest_->ResolveKey(p->url, &mapped_url, | 262 if (!manifest_->ResolveKey(p->url, &mapped_url, |
| 264 &pnacl_options, p->error_info)) { | 263 &pnacl_options, p->error_info)) { |
| 265 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); | 264 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); |
| 266 // Failed, and error_info has the details on what happened. Wake | 265 // Failed, and error_info has the details on what happened. Wake |
| 267 // up requesting thread -- we are done. | 266 // up requesting thread -- we are done. |
| 268 nacl::MutexLocker take(&mu_); | 267 nacl::MutexLocker take(&mu_); |
| 269 *p->op_complete_ptr = true; // done... | 268 *p->op_complete_ptr = true; // done... |
| 270 *p->out_desc = -1; // but failed. | 269 p->file_info->desc = -1; // but failed. |
| 271 NaClXCondVarBroadcast(&cv_); | 270 NaClXCondVarBroadcast(&cv_); |
| 272 return; | 271 return; |
| 273 } | 272 } |
| 274 NaClLog(4, | 273 NaClLog(4, |
| 275 "OpenManifestEntry_MainThreadContinuation: " | 274 "OpenManifestEntry_MainThreadContinuation: " |
| 276 "ResolveKey: %s -> %s (pnacl_translate(%d))\n", | 275 "ResolveKey: %s -> %s (pnacl_translate(%d))\n", |
| 277 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate()); | 276 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate()); |
| 278 | 277 |
| 279 open_cont = new OpenManifestEntryResource(*p); // copy ctor! | 278 open_cont = new OpenManifestEntryResource(*p); // copy ctor! |
| 280 CHECK(open_cont != NULL); | 279 CHECK(open_cont != NULL); |
| 281 open_cont->url = mapped_url; | 280 open_cont->url = mapped_url; |
| 282 if (!pnacl_options.translate()) { | 281 if (!pnacl_options.translate()) { |
| 283 pp::CompletionCallback stream_cc = WeakRefNewCallback( | 282 pp::CompletionCallback stream_cc = WeakRefNewCallback( |
| 284 anchor_, | 283 anchor_, |
| 285 this, | 284 this, |
| 286 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, | 285 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, |
| 287 open_cont); | 286 open_cont); |
| 288 // Normal files. | 287 // Normal files. |
| 289 if (!PnaclUrls::IsPnaclComponent(mapped_url)) { | 288 if (!PnaclUrls::IsPnaclComponent(mapped_url)) { |
| 290 if (!plugin_->StreamAsFile(mapped_url, | 289 if (!plugin_->StreamAsFile(mapped_url, |
| 291 stream_cc.pp_completion_callback())) { | 290 stream_cc.pp_completion_callback())) { |
| 292 NaClLog(4, | 291 NaClLog(4, |
| 293 "OpenManifestEntry_MainThreadContinuation: " | 292 "OpenManifestEntry_MainThreadContinuation: " |
| 294 "StreamAsFile failed\n"); | 293 "StreamAsFile failed\n"); |
| 295 nacl::MutexLocker take(&mu_); | 294 nacl::MutexLocker take(&mu_); |
| 296 *p->op_complete_ptr = true; // done... | 295 *p->op_complete_ptr = true; // done... |
| 297 *p->out_desc = -1; // but failed. | 296 p->file_info->desc = -1; // but failed. |
| 298 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 297 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 299 "ServiceRuntime: StreamAsFile failed"); | 298 "ServiceRuntime: StreamAsFile failed"); |
| 300 NaClXCondVarBroadcast(&cv_); | 299 NaClXCondVarBroadcast(&cv_); |
| 301 return; | 300 return; |
| 302 } | 301 } |
| 303 NaClLog(4, | 302 NaClLog(4, |
| 304 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); | 303 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); |
| 305 } else { | 304 } else { |
| 306 // Special PNaCl support files, that are installed on the | 305 // Special PNaCl support files, that are installed on the |
| 307 // user machine. | 306 // user machine. |
| 308 int32_t fd = PnaclResources::GetPnaclFD( | 307 int32_t fd = PnaclResources::GetPnaclFD( |
| 309 plugin_, | 308 plugin_, |
| 310 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str()); | 309 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str()); |
| 311 if (fd < 0) { | 310 if (fd < 0) { |
| 312 // We should check earlier if the pnacl component wasn't installed | 311 // We should check earlier if the pnacl component wasn't installed |
| 313 // yet. At this point, we can't do much anymore, so just continue | 312 // yet. At this point, we can't do much anymore, so just continue |
| 314 // with an invalid fd. | 313 // with an invalid fd. |
| 315 NaClLog(4, | 314 NaClLog(4, |
| 316 "OpenManifestEntry_MainThreadContinuation: " | 315 "OpenManifestEntry_MainThreadContinuation: " |
| 317 "GetReadonlyPnaclFd failed\n"); | 316 "GetReadonlyPnaclFd failed\n"); |
| 318 // TODO(jvoung): Separate the error codes? | 317 // TODO(jvoung): Separate the error codes? |
| 319 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 318 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 320 "ServiceRuntime: GetPnaclFd failed"); | 319 "ServiceRuntime: GetPnaclFd failed"); |
| 321 } | 320 } |
| 322 nacl::MutexLocker take(&mu_); | 321 nacl::MutexLocker take(&mu_); |
| 323 *p->op_complete_ptr = true; // done! | 322 *p->op_complete_ptr = true; // done! |
| 324 *p->out_desc = fd; | 323 // TODO(ncbray): more info for faster validation? |
| 324 p->file_info->desc = fd; | |
| 325 NaClXCondVarBroadcast(&cv_); | 325 NaClXCondVarBroadcast(&cv_); |
| 326 NaClLog(4, | 326 NaClLog(4, |
| 327 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n"); | 327 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n"); |
| 328 } | 328 } |
| 329 } else { | 329 } else { |
| 330 // Requires PNaCl translation. | 330 // Requires PNaCl translation. |
| 331 NaClLog(4, | 331 NaClLog(4, |
| 332 "OpenManifestEntry_MainThreadContinuation: " | 332 "OpenManifestEntry_MainThreadContinuation: " |
| 333 "pulling down and translating.\n"); | 333 "pulling down and translating.\n"); |
| 334 if (plugin_->nacl_interface()->IsPnaclEnabled()) { | 334 if (plugin_->nacl_interface()->IsPnaclEnabled()) { |
| 335 pp::CompletionCallback translate_callback = | 335 pp::CompletionCallback translate_callback = |
| 336 WeakRefNewCallback( | 336 WeakRefNewCallback( |
| 337 anchor_, | 337 anchor_, |
| 338 this, | 338 this, |
| 339 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation, | 339 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation, |
| 340 open_cont); | 340 open_cont); |
| 341 // Will always call the callback on success or failure. | 341 // Will always call the callback on success or failure. |
| 342 pnacl_coordinator_.reset( | 342 pnacl_coordinator_.reset( |
| 343 PnaclCoordinator::BitcodeToNative(plugin_, | 343 PnaclCoordinator::BitcodeToNative(plugin_, |
| 344 mapped_url, | 344 mapped_url, |
| 345 pnacl_options, | 345 pnacl_options, |
| 346 translate_callback)); | 346 translate_callback)); |
| 347 } else { | 347 } else { |
| 348 nacl::MutexLocker take(&mu_); | 348 nacl::MutexLocker take(&mu_); |
| 349 *p->op_complete_ptr = true; // done... | 349 *p->op_complete_ptr = true; // done... |
| 350 *p->out_desc = -1; // but failed. | 350 p->file_info->desc = -1; // but failed. |
| 351 p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED, | 351 p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED, |
| 352 "ServiceRuntime: GetPnaclFd failed -- pnacl not " | 352 "ServiceRuntime: GetPnaclFd failed -- pnacl not " |
| 353 "enabled with --enable-pnacl."); | 353 "enabled with --enable-pnacl."); |
| 354 NaClXCondVarBroadcast(&cv_); | 354 NaClXCondVarBroadcast(&cv_); |
| 355 return; | 355 return; |
| 356 } | 356 } |
| 357 } | 357 } |
| 358 // p is deleted automatically | 358 // p is deleted automatically |
| 359 } | 359 } |
| 360 | 360 |
| 361 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( | 361 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( |
| 362 OpenManifestEntryResource* p, | 362 OpenManifestEntryResource* p, |
| 363 int32_t result) { | 363 int32_t result) { |
| 364 NaClLog(4, | 364 NaClLog(4, |
| 365 "Entered StreamAsFile_MainThreadContinuation\n"); | 365 "Entered StreamAsFile_MainThreadContinuation\n"); |
| 366 | 366 |
| 367 nacl::MutexLocker take(&mu_); | 367 nacl::MutexLocker take(&mu_); |
| 368 if (result == PP_OK) { | 368 if (result == PP_OK) { |
| 369 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetPOSIXFileDesc(%s)\n", | 369 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n", |
| 370 p->url.c_str()); | 370 p->url.c_str()); |
| 371 *p->out_desc = plugin_->GetPOSIXFileDesc(p->url); | 371 *p->file_info = plugin_->GetFileInfo(p->url); |
| 372 | |
| 372 NaClLog(4, | 373 NaClLog(4, |
| 373 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", | 374 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", |
| 374 *p->out_desc); | 375 p->file_info->desc); |
| 375 } else { | 376 } else { |
| 376 NaClLog(4, | 377 NaClLog(4, |
| 377 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); | 378 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); |
| 378 *p->out_desc = -1; | 379 p->file_info->desc = -1; |
| 379 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 380 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 380 "Plugin StreamAsFile failed at callback"); | 381 "Plugin StreamAsFile failed at callback"); |
| 381 } | 382 } |
| 382 *p->op_complete_ptr = true; | 383 *p->op_complete_ptr = true; |
| 383 NaClXCondVarBroadcast(&cv_); | 384 NaClXCondVarBroadcast(&cv_); |
| 384 } | 385 } |
| 385 | 386 |
| 386 | 387 |
| 387 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation( | 388 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation( |
| 388 OpenManifestEntryResource* p, | 389 OpenManifestEntryResource* p, |
| 389 int32_t result) { | 390 int32_t result) { |
| 390 NaClLog(4, | 391 NaClLog(4, |
| 391 "Entered BitcodeTranslate_MainThreadContinuation\n"); | 392 "Entered BitcodeTranslate_MainThreadContinuation\n"); |
| 392 | 393 |
| 393 nacl::MutexLocker take(&mu_); | 394 nacl::MutexLocker take(&mu_); |
| 394 if (result == PP_OK) { | 395 if (result == PP_OK) { |
| 395 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is | 396 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is |
| 396 // a host IO desc and doing a downcast. Once the ReverseInterface | 397 // a host IO desc and doing a downcast. Once the ReverseInterface |
| 397 // accepts NaClDescs we can avoid this downcast. | 398 // accepts NaClDescs we can avoid this downcast. |
| 398 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc(); | 399 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc(); |
| 399 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc; | 400 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc; |
| 400 *p->out_desc = ndiodp->hd->d; | 401 p->file_info->desc = ndiodp->hd->d; |
| 401 pnacl_coordinator_.reset(NULL); | 402 pnacl_coordinator_.reset(NULL); |
| 402 NaClLog(4, | 403 NaClLog(4, |
| 403 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n", | 404 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n", |
| 404 *p->out_desc); | 405 p->file_info->desc); |
| 405 } else { | 406 } else { |
| 406 NaClLog(4, | 407 NaClLog(4, |
| 407 "BitcodeTranslate_MainThreadContinuation: !PP_OK, " | 408 "BitcodeTranslate_MainThreadContinuation: !PP_OK, " |
| 408 "setting desc -1\n"); | 409 "setting desc -1\n"); |
| 409 *p->out_desc = -1; | 410 p->file_info->desc = -1; |
| 410 // Error should have been reported by pnacl coordinator. | 411 // Error should have been reported by pnacl coordinator. |
| 411 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n"); | 412 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n"); |
| 412 } | 413 } |
| 413 *p->op_complete_ptr = true; | 414 *p->op_complete_ptr = true; |
| 414 NaClXCondVarBroadcast(&cv_); | 415 NaClXCondVarBroadcast(&cv_); |
| 415 } | 416 } |
| 416 | 417 |
| 417 | 418 |
| 418 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { | 419 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { |
| 419 bool op_complete = false; | 420 bool op_complete = false; |
| (...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 846 | 847 |
| 847 nacl::string ServiceRuntime::GetCrashLogOutput() { | 848 nacl::string ServiceRuntime::GetCrashLogOutput() { |
| 848 if (NULL != subprocess_.get()) { | 849 if (NULL != subprocess_.get()) { |
| 849 return subprocess_->GetCrashLogOutput(); | 850 return subprocess_->GetCrashLogOutput(); |
| 850 } else { | 851 } else { |
| 851 return std::string(); | 852 return std::string(); |
| 852 } | 853 } |
| 853 } | 854 } |
| 854 | 855 |
| 855 } // namespace plugin | 856 } // namespace plugin |
| OLD | NEW |