普通文本  |  200行  |  6.67 KB

/*
 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "ppapi/native_client/src/trusted/plugin/srpc_client.h"

#include <string.h>

#include "native_client/src/shared/platform/nacl_log.h"
#include "ppapi/native_client/src/trusted/plugin/plugin.h"
#include "ppapi/native_client/src/trusted/plugin/srpc_params.h"
#include "ppapi/native_client/src/trusted/plugin/utility.h"

namespace plugin {

typedef bool (*RpcFunction)(void* obj, SrpcParams* params);

// MethodInfo records the method names and type signatures of an SRPC server.
class MethodInfo {
 public:
  // statically defined method - called through a pointer
  MethodInfo(const RpcFunction function_ptr,
             const char* name,
             const char* ins,
             const char* outs,
             // index is set to UINT_MAX for methods implemented by the plugin,
             // All methods implemented by nacl modules have indexes
             // that are lower than UINT_MAX.
             const uint32_t index = UINT_MAX) :
    function_ptr_(function_ptr),
    name_(STRDUP(name)),
    ins_(STRDUP(ins)),
    outs_(STRDUP(outs)),
    index_(index) { }

  ~MethodInfo() {
    free(reinterpret_cast<void*>(name_));
    free(reinterpret_cast<void*>(ins_));
    free(reinterpret_cast<void*>(outs_));
  }

  RpcFunction function_ptr() const { return function_ptr_; }
  char* name() const { return name_; }
  char* ins() const { return ins_; }
  char* outs() const { return outs_; }
  uint32_t index() const { return index_; }

 private:
  NACL_DISALLOW_COPY_AND_ASSIGN(MethodInfo);
  RpcFunction function_ptr_;
  char* name_;
  char* ins_;
  char* outs_;
  uint32_t index_;
};

SrpcClient::SrpcClient()
    : srpc_channel_initialised_(false) {
  PLUGIN_PRINTF(("SrpcClient::SrpcClient (this=%p)\n",
                 static_cast<void*>(this)));
  NaClSrpcChannelInitialize(&srpc_channel_);
}

SrpcClient* SrpcClient::New(nacl::DescWrapper* wrapper) {
  nacl::scoped_ptr<SrpcClient> srpc_client(new SrpcClient());
  if (!srpc_client->Init(wrapper)) {
    PLUGIN_PRINTF(("SrpcClient::New (SrpcClient::Init failed)\n"));
    return NULL;
  }
  return srpc_client.release();
}

bool SrpcClient::Init(nacl::DescWrapper* wrapper) {
  PLUGIN_PRINTF(("SrpcClient::Init (this=%p, wrapper=%p)\n",
                 static_cast<void*>(this),
                 static_cast<void*>(wrapper)));
  // Open the channel to pass RPC information back and forth
  if (!NaClSrpcClientCtor(&srpc_channel_, wrapper->desc())) {
    return false;
  }
  srpc_channel_initialised_ = true;
  PLUGIN_PRINTF(("SrpcClient::Init (Ctor worked)\n"));
  // Record the method names in a convenient way for later dispatches.
  GetMethods();
  PLUGIN_PRINTF(("SrpcClient::Init (GetMethods worked)\n"));
  return true;
}

SrpcClient::~SrpcClient() {
  PLUGIN_PRINTF(("SrpcClient::~SrpcClient (this=%p, has_srpc_channel=%d)\n",
                 static_cast<void*>(this), srpc_channel_initialised_));
  // And delete the connection.
  if (srpc_channel_initialised_) {
    PLUGIN_PRINTF(("SrpcClient::~SrpcClient (destroying srpc_channel)\n"));
    NaClSrpcDtor(&srpc_channel_);
  }
  for (Methods::iterator iter = methods_.begin();
       iter != methods_.end();
       ++iter) {
    delete iter->second;
  }
  PLUGIN_PRINTF(("SrpcClient::~SrpcClient (return)\n"));
}

void SrpcClient::GetMethods() {
  PLUGIN_PRINTF(("SrpcClient::GetMethods (this=%p)\n",
                 static_cast<void*>(this)));
  if (NULL == srpc_channel_.client) {
    return;
  }
  uint32_t method_count = NaClSrpcServiceMethodCount(srpc_channel_.client);
  // Intern the methods into a mapping from identifiers to MethodInfo.
  for (uint32_t i = 0; i < method_count; ++i) {
    int retval;
    const char* method_name;
    const char* input_types;
    const char* output_types;

    retval = NaClSrpcServiceMethodNameAndTypes(srpc_channel_.client,
                                               i,
                                               &method_name,
                                               &input_types,
                                               &output_types);
    if (!retval) {
      return;
    }
    if (!IsValidIdentifierString(method_name, NULL)) {
      // If name is not an ECMAScript identifier, do not enter it into the
      // methods_ table.
      continue;
    }
    MethodInfo* method_info =
        new MethodInfo(NULL, method_name, input_types, output_types, i);
    if (NULL == method_info) {
      return;
    }
    // Install in the map only if successfully read.
    methods_[method_name] = method_info;
  }
}

bool SrpcClient::HasMethod(const nacl::string& method_name) {
  bool has_method = (NULL != methods_[method_name]);
  PLUGIN_PRINTF((
      "SrpcClient::HasMethod (this=%p, method_name='%s', return %d)\n",
      static_cast<void*>(this), method_name.c_str(), has_method));
  return has_method;
}

bool SrpcClient::InitParams(const nacl::string& method_name,
                            SrpcParams* params) {
  MethodInfo* method_info = methods_[method_name];
  if (method_info) {
    return params->Init(method_info->ins(), method_info->outs());
  }
  return false;
}

bool SrpcClient::Invoke(const nacl::string& method_name, SrpcParams* params) {
  // It would be better if we could set the exception on each detailed failure
  // case.  However, there are calls to Invoke from within the plugin itself,
  // and these could leave residual exceptions pending.  This seems to be
  // happening specifically with hard_shutdowns.
  PLUGIN_PRINTF(("SrpcClient::Invoke (this=%p, method_name='%s', params=%p)\n",
                 static_cast<void*>(this),
                 method_name.c_str(),
                 static_cast<void*>(params)));

  // Ensure Invoke was called with a method name that has a binding.
  if (NULL == methods_[method_name]) {
    PLUGIN_PRINTF(("SrpcClient::Invoke (ident not in methods_)\n"));
    return false;
  }

  PLUGIN_PRINTF(("SrpcClient::Invoke (sending the rpc)\n"));
  // Call the method
  last_error_ = NaClSrpcInvokeV(&srpc_channel_,
                                methods_[method_name]->index(),
                                params->ins(),
                                params->outs());
  PLUGIN_PRINTF(("SrpcClient::Invoke (response=%d)\n", last_error_));
  if (NACL_SRPC_RESULT_OK != last_error_) {
    PLUGIN_PRINTF(("SrpcClient::Invoke (err='%s', return 0)\n",
                   NaClSrpcErrorString(last_error_)));
    return false;
  }

  PLUGIN_PRINTF(("SrpcClient::Invoke (return 1)\n"));
  return true;
}

void SrpcClient::AttachService(NaClSrpcService* service, void* instance_data) {
  srpc_channel_.server = service;
  srpc_channel_.server_instance_data = instance_data;
}

}  // namespace plugin