Chromium Code Reviews| Index: extensions/renderer/api_signature.cc |
| diff --git a/extensions/renderer/api_signature.cc b/extensions/renderer/api_signature.cc |
| index 0edca2f9bf199545c6b3e8f5ec9c65f140e221ca..6d83c38ccdb41d690dce05e09c187619c281a7d1 100644 |
| --- a/extensions/renderer/api_signature.cc |
| +++ b/extensions/renderer/api_signature.cc |
| @@ -10,24 +10,121 @@ |
| namespace extensions { |
| -APISignature::APISignature(const base::ListValue& specification) { |
| - signature_.reserve(specification.GetSize()); |
| - for (const auto& value : specification) { |
| - const base::DictionaryValue* param = nullptr; |
| - CHECK(value->GetAsDictionary(¶m)); |
| - signature_.push_back(base::MakeUnique<ArgumentSpec>(*param)); |
| +namespace { |
| + |
| +// A class to help with argument parsing. Note that this uses v8::Locals and |
| +// const&s because it's an implementation detail of the APISignature; this |
| +// should *only* be used directly on the stack! |
| +class ArgumentParser { |
| + public: |
| + ArgumentParser(const std::vector<std::unique_ptr<ArgumentSpec>>& signature, |
| + gin::Arguments* arguments, |
| + const ArgumentSpec::RefMap& type_refs, |
| + std::string* error) |
| + : context_(arguments->isolate()->GetCurrentContext()), |
| + signature_(signature), |
| + arguments_(arguments), |
| + type_refs_(type_refs), |
| + error_(error) {} |
| + |
| + // Tries to parse the arguments against the expected signature, populating a |
| + // resulting list as it goes. |
|
lazyboy
2016/12/27 23:48:44
nittynit: I find "as it goes" a bit confusing, hin
Devlin
2016/12/28 00:56:18
Sounds good to me; done.
|
| + bool ParseArguments(); |
| + |
| + protected: |
| + v8::Isolate* GetIsolate() { return context_->GetIsolate(); } |
| + |
| + private: |
| + // Attempts to match the next argument to the given |spec|. |
| + // If the next argument does not match and |spec| is optional, uses a null |
| + // value. |
| + // Returns true on success. |
| + bool ParseArgument(const ArgumentSpec& spec); |
| + |
| + // Attempts to parse the callback from the given |spec|. Returns true on |
| + // success. |
| + bool ParseCallback(const ArgumentSpec& spec); |
| + |
| + // Adds a null value to the parsed arguments. |
| + virtual void AddNull() = 0; |
| + // Returns a base::Value to be populated during argument matching. |
| + virtual std::unique_ptr<base::Value>* GetBuffer() = 0; |
| + // Adds a new parsed argument. |
| + virtual void AddParsedArgument(v8::Local<v8::Value> value) = 0; |
| + // Adds the parsed callback. |
| + virtual void SetCallback(v8::Local<v8::Function> callback) = 0; |
| + |
| + v8::Local<v8::Context> context_; |
| + const std::vector<std::unique_ptr<ArgumentSpec>>& signature_; |
| + gin::Arguments* arguments_; |
| + const ArgumentSpec::RefMap& type_refs_; |
| + std::string* error_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ArgumentParser); |
| +}; |
| + |
| +class V8ArgumentParser : public ArgumentParser { |
| + public: |
| + V8ArgumentParser(const std::vector<std::unique_ptr<ArgumentSpec>>& signature, |
| + gin::Arguments* arguments, |
| + const ArgumentSpec::RefMap& type_refs, |
| + std::string* error, |
| + std::vector<v8::Local<v8::Value>>* values) |
| + : ArgumentParser(signature, arguments, type_refs, error), |
| + values_(values) {} |
| + |
| + private: |
| + void AddNull() override { values_->push_back(v8::Null(GetIsolate())); } |
| + std::unique_ptr<base::Value>* GetBuffer() override { return nullptr; } |
| + void AddParsedArgument(v8::Local<v8::Value> value) override { |
| + values_->push_back(value); |
| + } |
| + void SetCallback(v8::Local<v8::Function> callback) override { |
| + values_->push_back(callback); |
| } |
| -} |
| -APISignature::~APISignature() {} |
| + std::vector<v8::Local<v8::Value>>* values_; |
| -std::unique_ptr<base::ListValue> APISignature::ParseArguments( |
| - gin::Arguments* arguments, |
| - const ArgumentSpec::RefMap& type_refs, |
| - v8::Local<v8::Function>* callback_out, |
| - std::string* error) const { |
| - auto results = base::MakeUnique<base::ListValue>(); |
| + DISALLOW_COPY_AND_ASSIGN(V8ArgumentParser); |
| +}; |
| + |
| +class BaseValueArgumentParser : public ArgumentParser { |
| + public: |
| + BaseValueArgumentParser( |
| + const std::vector<std::unique_ptr<ArgumentSpec>>& signature, |
| + gin::Arguments* arguments, |
| + const ArgumentSpec::RefMap& type_refs, |
| + std::string* error, |
| + base::ListValue* list_value) |
| + : ArgumentParser(signature, arguments, type_refs, error), |
| + list_value_(list_value) {} |
| + v8::Local<v8::Function> callback() { return callback_; } |
| + |
| + private: |
| + void AddNull() override { |
| + list_value_->Append(base::Value::CreateNullValue()); |
| + } |
| + std::unique_ptr<base::Value>* GetBuffer() override { return &last_arg_; } |
| + void AddParsedArgument(v8::Local<v8::Value> value) override { |
| + // The corresponding base::Value is expected to have been stored in |
| + // |last_arg_| already. |
| + DCHECK(last_arg_); |
| + list_value_->Append(std::move(last_arg_)); |
| + last_arg_.reset(); |
| + } |
| + void SetCallback(v8::Local<v8::Function> callback) override { |
| + callback_ = callback; |
| + } |
| + |
| + base::ListValue* list_value_; |
| + std::unique_ptr<base::Value> last_arg_; |
| + v8::Local<v8::Function> callback_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BaseValueArgumentParser); |
| +}; |
| + |
| +bool ArgumentParser::ParseArguments() { |
| // TODO(devlin): This is how extension APIs have always determined if a |
| // function has a callback, but it seems a little silly. In the long run (once |
| // signatures are generated), it probably makes sense to indicate this |
| @@ -36,84 +133,114 @@ std::unique_ptr<base::ListValue> APISignature::ParseArguments( |
| !signature_.empty() && |
| signature_.back()->type() == ArgumentType::FUNCTION; |
| - v8::Local<v8::Context> context = arguments->isolate()->GetCurrentContext(); |
| - |
| size_t end_size = |
| signature_has_callback ? signature_.size() - 1 : signature_.size(); |
| for (size_t i = 0; i < end_size; ++i) { |
| - std::unique_ptr<base::Value> parsed = |
| - ParseArgument(*signature_[i], context, arguments, type_refs, error); |
| - if (!parsed) |
| - return nullptr; |
| - results->Append(std::move(parsed)); |
| + if (!ParseArgument(*signature_[i])) |
| + return false; |
| } |
| - v8::Local<v8::Function> callback_value; |
| - if (signature_has_callback && |
| - !ParseCallback(arguments, *signature_.back(), error, &callback_value)) { |
| - return nullptr; |
| - } |
| + if (signature_has_callback && !ParseCallback(*signature_.back())) |
| + return false; |
| - if (!arguments->PeekNext().IsEmpty()) |
| - return nullptr; // Extra arguments aren't allowed. |
| + if (!arguments_->PeekNext().IsEmpty()) |
| + return false; // Extra arguments aren't allowed. |
| - *callback_out = callback_value; |
| - return results; |
| + return true; |
| } |
| -std::unique_ptr<base::Value> APISignature::ParseArgument( |
| - const ArgumentSpec& spec, |
| - v8::Local<v8::Context> context, |
| - gin::Arguments* arguments, |
| - const ArgumentSpec::RefMap& type_refs, |
| - std::string* error) const { |
| - v8::Local<v8::Value> value = arguments->PeekNext(); |
| +bool ArgumentParser::ParseArgument(const ArgumentSpec& spec) { |
| + v8::Local<v8::Value> value = arguments_->PeekNext(); |
| if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { |
| if (!spec.optional()) { |
| - *error = "Missing required argument: " + spec.name(); |
| - return nullptr; |
| + *error_ = "Missing required argument: " + spec.name(); |
| + return false; |
| } |
| // This is safe to call even if |arguments| is at the end (which can happen |
| // if n optional arguments are omitted at the end of the signature). |
| - arguments->Skip(); |
| - return base::Value::CreateNullValue(); |
| + arguments_->Skip(); |
| + |
| + AddNull(); |
| + return true; |
| } |
| - std::unique_ptr<base::Value> result = |
| - spec.ConvertArgument(context, value, type_refs, error); |
| - if (!result) { |
| + if (!spec.ParseArgument(context_, value, type_refs_, GetBuffer(), error_)) { |
| if (!spec.optional()) { |
| - *error = "Missing required argument: " + spec.name(); |
| - return nullptr; |
| + *error_ = "Missing required argument: " + spec.name(); |
| + return false; |
| } |
| - return base::Value::CreateNullValue(); |
| + |
| + AddNull(); |
| + return true; |
| } |
| - arguments->Skip(); |
| - return result; |
| + arguments_->Skip(); |
| + AddParsedArgument(value); |
| + return true; |
| } |
| -bool APISignature::ParseCallback(gin::Arguments* arguments, |
| - const ArgumentSpec& callback_spec, |
| - std::string* error, |
| - v8::Local<v8::Function>* callback_out) const { |
| - v8::Local<v8::Value> value = arguments->PeekNext(); |
| +bool ArgumentParser::ParseCallback(const ArgumentSpec& spec) { |
| + v8::Local<v8::Value> value = arguments_->PeekNext(); |
| if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { |
| - if (!callback_spec.optional()) { |
| - *error = "Missing required argument: " + callback_spec.name(); |
| + if (!spec.optional()) { |
| + *error_ = "Missing required argument: " + spec.name(); |
| return false; |
| } |
| - arguments->Skip(); |
| + arguments_->Skip(); |
| return true; |
| } |
| if (!value->IsFunction()) { |
| - *error = "Argument is wrong type: " + callback_spec.name(); |
| + *error_ = "Argument is wrong type: " + spec.name(); |
| return false; |
| } |
| - *callback_out = value.As<v8::Function>(); |
| - arguments->Skip(); |
| + arguments_->Skip(); |
| + SetCallback(value.As<v8::Function>()); |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +APISignature::APISignature(const base::ListValue& specification) { |
| + signature_.reserve(specification.GetSize()); |
| + for (const auto& value : specification) { |
| + const base::DictionaryValue* param = nullptr; |
| + CHECK(value->GetAsDictionary(¶m)); |
| + signature_.push_back(base::MakeUnique<ArgumentSpec>(*param)); |
| + } |
| +} |
| + |
| +APISignature::~APISignature() {} |
| + |
| +bool APISignature::ParseArgumentsToV8(gin::Arguments* arguments, |
| + const ArgumentSpec::RefMap& type_refs, |
| + std::vector<v8::Local<v8::Value>>* v8_out, |
| + std::string* error) const { |
| + DCHECK(v8_out); |
| + std::vector<v8::Local<v8::Value>> v8_values; |
| + V8ArgumentParser parser(signature_, arguments, type_refs, error, &v8_values); |
| + if (!parser.ParseArguments()) |
| + return false; |
| + *v8_out = std::move(v8_values); |
| + return true; |
| +} |
| + |
| +bool APISignature::ParseArgumentsToJSON( |
| + gin::Arguments* arguments, |
| + const ArgumentSpec::RefMap& type_refs, |
| + std::unique_ptr<base::ListValue>* json_out, |
| + v8::Local<v8::Function>* callback_out, |
| + std::string* error) const { |
| + DCHECK(json_out); |
| + DCHECK(callback_out); |
| + std::unique_ptr<base::ListValue> json = base::MakeUnique<base::ListValue>(); |
| + BaseValueArgumentParser parser( |
| + signature_, arguments, type_refs, error, json.get()); |
| + if (!parser.ParseArguments()) |
| + return false; |
| + *json_out = std::move(json); |
| + *callback_out = parser.callback(); |
| return true; |
| } |