Commit ec95fdfd authored by abarth@chromium.org's avatar abarth@chromium.org

[Mojo] Improve JavaScript API for MojoReadMessage

Originally, I tried to make the JavaScript bindings for the core Mojo APIs a
literal translation of the C APIs, but that makes MojoReadMessage a bit awkward
because C needs to worry much more about buffer allocation than JavaScript. In
building towards using the hello_world_service, I found that it was much easier
to push the MojoReadMessage pattern from connector.cc into C++ and make the
JavaScript API a bit more idiomatic by just returning an ArrayBuffer for the
message and an Array of handles.

BUG=317398

Review URL: https://codereview.chromium.org/83143002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236999 0039d316-1c4b-4281-b951-d872f2087c98
parent 8b4e19b1
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "v8/include/v8.h" #include "v8/include/v8.h"
using v8::ArrayBuffer;
using v8::Boolean; using v8::Boolean;
using v8::External; using v8::External;
using v8::Function; using v8::Function;
...@@ -125,6 +126,19 @@ bool Converter<Handle<Object> >::FromV8(Handle<Value> val, ...@@ -125,6 +126,19 @@ bool Converter<Handle<Object> >::FromV8(Handle<Value> val,
return true; return true;
} }
Handle<Value> Converter<Handle<ArrayBuffer> >::ToV8(v8::Isolate* isolate,
Handle<ArrayBuffer> val) {
return val.As<Value>();
}
bool Converter<Handle<ArrayBuffer> >::FromV8(Handle<Value> val,
Handle<ArrayBuffer>* out) {
if (!val->IsArrayBuffer())
return false;
*out = Handle<ArrayBuffer>::Cast(val);
return true;
}
Handle<Value> Converter<Handle<External> >::ToV8(v8::Isolate* isolate, Handle<Value> Converter<Handle<External> >::ToV8(v8::Isolate* isolate,
Handle<External> val) { Handle<External> val) {
return val.As<Value>(); return val.As<Value>();
......
...@@ -87,6 +87,14 @@ struct Converter<v8::Handle<v8::Object> > { ...@@ -87,6 +87,14 @@ struct Converter<v8::Handle<v8::Object> > {
v8::Handle<v8::Object>* out); v8::Handle<v8::Object>* out);
}; };
template<>
struct Converter<v8::Handle<v8::ArrayBuffer> > {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
v8::Handle<v8::ArrayBuffer> val);
static bool FromV8(v8::Handle<v8::Value> val,
v8::Handle<v8::ArrayBuffer>* out);
};
template<> template<>
struct Converter<v8::Handle<v8::External> > { struct Converter<v8::Handle<v8::External> > {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "gin/converter.h" #include "gin/converter.h"
#include "gin/per_isolate_data.h" #include "gin/per_isolate_data.h"
#include "gin/public/wrapper_info.h" #include "gin/public/wrapper_info.h"
#include "gin/try_catch.h"
using v8::Context; using v8::Context;
using v8::External; using v8::External;
...@@ -91,19 +92,6 @@ Handle<String> GetHiddenValueKey(Isolate* isolate) { ...@@ -91,19 +92,6 @@ Handle<String> GetHiddenValueKey(Isolate* isolate) {
return StringToSymbol(isolate, "::gin::ModuleRegistry"); return StringToSymbol(isolate, "::gin::ModuleRegistry");
} }
std::string GetImplicitModuleName(const std::string& explicit_name) {
if (!explicit_name.empty())
return explicit_name;
std::string implicit_name;
Handle<StackTrace> trace = StackTrace::CurrentStackTrace(1);
if (!trace->GetFrameCount())
return implicit_name;
Handle<String> script_name = trace->GetFrame(0)->GetScriptName();
if (!script_name.IsEmpty())
ConvertFromV8(script_name, &implicit_name);
return implicit_name;
}
} // namespace } // namespace
ModuleRegistry::ModuleRegistry(Isolate* isolate) ModuleRegistry::ModuleRegistry(Isolate* isolate)
...@@ -196,11 +184,17 @@ void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) { ...@@ -196,11 +184,17 @@ void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) {
Handle<Function> factory; Handle<Function> factory;
if (ConvertFromV8(module, &factory)) { if (ConvertFromV8(module, &factory)) {
Handle<Object> global = isolate->GetCurrentContext()->Global(); Handle<Object> global = isolate->GetCurrentContext()->Global();
module = factory->Call(global, argc, argv.data()); {
// TODO(abarth): What should we do with exceptions? gin::TryCatch try_catch;
module = factory->Call(global, argc, argv.data());
if (try_catch.HasCaught())
return; // TODO(abarth): What should we do with the exception?
}
if (pending->id.empty())
ConvertFromV8(factory->GetScriptOrigin().ResourceName(), &pending->id);
} }
RegisterModule(isolate, GetImplicitModuleName(pending->id), module); RegisterModule(isolate, pending->id, module);
} }
bool ModuleRegistry::AttemptToLoad(Isolate* isolate, bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
......
include_rules = [ include_rules = [
"+base",
"+gin", "+gin",
"+testing", "+testing",
"+v8", "+v8",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "mojo/public/bindings/js/core.h" #include "mojo/public/bindings/js/core.h"
#include "base/logging.h"
#include "gin/arguments.h" #include "gin/arguments.h"
#include "gin/array_buffer.h" #include "gin/array_buffer.h"
#include "gin/converter.h" #include "gin/converter.h"
...@@ -62,22 +63,23 @@ void WaitMany(const v8::FunctionCallbackInfo<v8::Value>& info) { ...@@ -62,22 +63,23 @@ void WaitMany(const v8::FunctionCallbackInfo<v8::Value>& info) {
void CreateMessagePipe(const v8::FunctionCallbackInfo<v8::Value>& info) { void CreateMessagePipe(const v8::FunctionCallbackInfo<v8::Value>& info) {
gin::Arguments args(info); gin::Arguments args(info);
mojo::ScopedMessagePipeHandle handle_0; MojoHandle handle_0 = MOJO_HANDLE_INVALID;
mojo::ScopedMessagePipeHandle handle_1; MojoHandle handle_1 = MOJO_HANDLE_INVALID;
mojo::CreateMessagePipe(&handle_0, &handle_1); MojoResult result = MojoCreateMessagePipe(&handle_0, &handle_1);
CHECK(result == MOJO_RESULT_OK);
gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(info.GetIsolate()); gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(info.GetIsolate());
dictionary.Set("handle0", static_cast<mojo::Handle>(handle_0.release())); dictionary.Set("handle0", handle_0);
dictionary.Set("handle1", static_cast<mojo::Handle>(handle_1.release())); dictionary.Set("handle1", handle_1);
args.Return(dictionary); args.Return(dictionary);
} }
void WriteMessage(const v8::FunctionCallbackInfo<v8::Value>& info) { void WriteMessage(const v8::FunctionCallbackInfo<v8::Value>& info) {
gin::Arguments args(info); gin::Arguments args(info);
mojo::Handle handle; MojoHandle handle = MOJO_HANDLE_INVALID;
gin::ArrayBufferView buffer(args.isolate()); gin::ArrayBufferView buffer(args.isolate());
std::vector<mojo::Handle> handles; std::vector<MojoHandle> handles;
MojoWriteMessageFlags flags = MOJO_WRITE_MESSAGE_FLAG_NONE; MojoWriteMessageFlags flags = MOJO_WRITE_MESSAGE_FLAG_NONE;
if (!args.GetNext(&handle) || if (!args.GetNext(&handle) ||
...@@ -87,46 +89,61 @@ void WriteMessage(const v8::FunctionCallbackInfo<v8::Value>& info) { ...@@ -87,46 +89,61 @@ void WriteMessage(const v8::FunctionCallbackInfo<v8::Value>& info) {
return args.ThrowError(); return args.ThrowError();
} }
args.Return(mojo::WriteMessageRaw( args.Return(MojoWriteMessage(handle,
MessagePipeHandle(handle.value()), buffer.bytes(), buffer.bytes(),
static_cast<uint32_t>(buffer.num_bytes()), static_cast<uint32_t>(buffer.num_bytes()),
handles.empty() ? NULL : reinterpret_cast<const MojoHandle*>(&handles[0]), handles.empty() ? NULL : handles.data(),
static_cast<uint32_t>(handles.size()), flags)); static_cast<uint32_t>(handles.size()),
flags));
} }
void ReadMessage(const v8::FunctionCallbackInfo<v8::Value>& info) { void ReadMessage(const v8::FunctionCallbackInfo<v8::Value>& info) {
gin::Arguments args(info); gin::Arguments args(info);
mojo::Handle handle; MojoHandle handle = MOJO_HANDLE_INVALID;
gin::ArrayBufferView buffer(args.isolate());
uint32_t num_handles = 0;
MojoReadMessageFlags flags = MOJO_READ_MESSAGE_FLAG_NONE; MojoReadMessageFlags flags = MOJO_READ_MESSAGE_FLAG_NONE;
if (!args.GetNext(&handle) || if (!args.GetNext(&handle) ||
!args.GetNext(&buffer) ||
!args.GetNext(&num_handles) ||
!args.GetNext(&flags)) { !args.GetNext(&flags)) {
return args.ThrowError(); return args.ThrowError();
} }
uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes()); uint32_t num_bytes = 0;
std::vector<mojo::Handle> handles(num_handles); uint32_t num_handles = 0;
MojoResult result = mojo::ReadMessageRaw( MojoResult result = MojoReadMessage(
MessagePipeHandle(handle.value()), buffer.bytes(), &num_bytes, handle, NULL, &num_bytes, NULL, &num_handles, flags);
handles.empty() ? NULL : reinterpret_cast<MojoHandle*>(&handles[0]), if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
&num_handles, flags); gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(
handles.resize(num_handles); info.GetIsolate());
dictionary.Set("result", result);
args.Return(dictionary);
}
v8::Handle<v8::ArrayBuffer> array_buffer = v8::ArrayBuffer::New(num_bytes);
std::vector<MojoHandle> handles(num_handles);
gin::ArrayBuffer buffer(args.isolate());
ConvertFromV8(array_buffer, &buffer);
CHECK(buffer.num_bytes() == num_bytes);
result = MojoReadMessage(handle,
buffer.bytes(),
&num_bytes,
handles.empty() ? NULL : handles.data(),
&num_handles,
flags);
CHECK(buffer.num_bytes() == num_bytes);
CHECK(handles.size() == num_handles);
// TODO(abarth): We should benchmark this codepath to make sure it's ok to
// allocate all this memory on each read.
gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(info.GetIsolate()); gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(info.GetIsolate());
dictionary.Set("result", result); dictionary.Set("result", result);
dictionary.Set("bytesRead", num_bytes); dictionary.Set("buffer", array_buffer);
dictionary.Set("handles", handles); dictionary.Set("handles", handles);
args.Return(dictionary); args.Return(dictionary);
} }
gin::WrapperInfo g_core_wrapper_info = { gin::kEmbedderNativeGin }; gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
} // namespace } // namespace
...@@ -135,7 +152,7 @@ const char Core::kModuleName[] = "mojo/public/bindings/js/core"; ...@@ -135,7 +152,7 @@ const char Core::kModuleName[] = "mojo/public/bindings/js/core";
v8::Local<v8::ObjectTemplate> Core::GetTemplate(v8::Isolate* isolate) { v8::Local<v8::ObjectTemplate> Core::GetTemplate(v8::Isolate* isolate) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
&g_core_wrapper_info); &g_wrapper_info);
if (templ.IsEmpty()) { if (templ.IsEmpty()) {
templ = v8::ObjectTemplate::New(); templ = v8::ObjectTemplate::New();
...@@ -211,7 +228,7 @@ v8::Local<v8::ObjectTemplate> Core::GetTemplate(v8::Isolate* isolate) { ...@@ -211,7 +228,7 @@ v8::Local<v8::ObjectTemplate> Core::GetTemplate(v8::Isolate* isolate) {
templ->Set(gin::StringToSymbol(isolate, "READ_MESSAGE_FLAG_MAY_DISCARD"), templ->Set(gin::StringToSymbol(isolate, "READ_MESSAGE_FLAG_MAY_DISCARD"),
gin::ConvertToV8(isolate, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); gin::ConvertToV8(isolate, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
data->SetObjectTemplate(&g_core_wrapper_info, templ); data->SetObjectTemplate(&g_wrapper_info, templ);
} }
return templ; return templ;
......
...@@ -42,20 +42,20 @@ define([ ...@@ -42,20 +42,20 @@ define([
var receiverData = new Uint8Array(50); var receiverData = new Uint8Array(50);
var mesage = core.readMessage( var read = core.readMessage(
pipe.handle1, receiverData, 10, pipe.handle1, core.READ_MESSAGE_FLAG_NONE)
core.READ_MESSAGE_FLAG_NONE)
gtest.expectEqual(read.result, core.RESULT_OK,
gtest.expectEqual(mesage.result, core.RESULT_OK, "read.result is " + read.result);
"mesage.result is " + mesage.result); gtest.expectEqual(read.buffer.byteLength, 42,
gtest.expectEqual(mesage.bytesRead, 42, "read.buffer.byteLength is " + read.buffer.byteLength);
"mesage.bytesRead is " + mesage.bytesRead); gtest.expectEqual(read.handles.length, 0,
gtest.expectEqual(mesage.handles.length, 0, "read.handles.length is " + read.handles.length);
"mesage.handles.length is " + mesage.handles.length);
var memory = new Uint8Array(read.buffer);
for (var i = 0; i < mesage.bytesRead; ++i) { for (var i = 0; i < memory.length; ++i) {
gtest.expectEqual(receiverData[i], (i * i) & 0xFF, gtest.expectEqual(memory[i], (i * i) & 0xFF,
"receiverData[" + i + "] is " + receiverData[i]); "memory[" + i + "] is " + memory[i]);
} }
} }
}); });
...@@ -29,9 +29,10 @@ ...@@ -29,9 +29,10 @@
'<(DEPTH)/mojo/public/bindings/generators/template_expander.py', '<(DEPTH)/mojo/public/bindings/generators/template_expander.py',
], ],
'outputs': [ 'outputs': [
'<(output_dir)/<(RULE_INPUT_ROOT).cc',
'<(output_dir)/<(RULE_INPUT_ROOT).h', '<(output_dir)/<(RULE_INPUT_ROOT).h',
'<(output_dir)/<(RULE_INPUT_ROOT).js',
'<(output_dir)/<(RULE_INPUT_ROOT)_internal.h', '<(output_dir)/<(RULE_INPUT_ROOT)_internal.h',
'<(output_dir)/<(RULE_INPUT_ROOT).cc',
], ],
'action': [ 'action': [
'python', '<@(mojom_bindings_generator)', 'python', '<@(mojom_bindings_generator)',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment