Commit 6c653757 authored by Scott Graham's avatar Scott Graham Committed by Commit Bot

Fuchsia: Implement request<Interface> in FIDL/JS

Fairly basic and the connection must be manually torn down for now, but
allows requesting an interface implemention from the JS side.

Bug: 883496
Change-Id: I8367fd50097403ee2f9911657afbabdd8d461a41
Reviewed-on: https://chromium-review.googlesource.com/c/1324929
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606663}
parent d96b4d74
......@@ -402,7 +402,7 @@ function %(name)s(%(param_names)s) {
compound = _ParseCompoundIdentifier(t.identifier)
name = _CompileCompoundIdentifier(compound)
return name + ('_Nullable' if t.nullable else '')
elif t.kind == fidl.TypeKind.HANDLE:
elif t.kind == fidl.TypeKind.HANDLE or t.kind == fidl.TypeKind.REQUEST:
return 'Handle'
elif t.kind == fidl.TypeKind.ARRAY:
element_ttname = self._CompileType(t.element_type)
......@@ -545,6 +545,30 @@ function %(proxy_name)s() {
this.channel = channel;
};
%(proxy_name)s.prototype.$is_bound = function() {
return this.channel != $ZX_HANDLE_INVALID;
};
%(proxy_name)s.prototype.$request = function() {
if (this.$is_bound())
throw "Proxy already bound";
var pair = $ZxChannelCreate();
if (pair.status != $ZX_OK)
throw "ChannelPair creation failed";
this.channel = pair.first;
return pair.second;
};
%(proxy_name)s.prototype.$close = function() {
if (!this.$is_bound())
return;
var status = $zx_handle_close(this.channel);
if (status !== $ZX_OK) {
throw "close handle failed";
}
this.channel = $ZX_HANDLE_INVALID;
};
''' % {
'name': name,
'proxy_name': proxy_name
......
......@@ -189,6 +189,22 @@ v8::Local<v8::Promise> ZxObjectWaitOne(gin::Arguments* args) {
return v8::Local<v8::Promise>();
}
v8::Local<v8::Value> ZxChannelCreate(gin::Arguments* args) {
zx_handle_t channel0, channel1;
zx_status_t status = zx_channel_create(0, &channel0, &channel1);
if (status != ZX_OK) {
return gin::DataObjectBuilder(args->isolate())
.Set("status", status)
.Build();
}
return gin::DataObjectBuilder(args->isolate())
.Set("status", status)
.Set("first", channel0)
.Set("second", channel1)
.Build();
}
zx_status_t ZxChannelWrite(gin::Arguments* args) {
zx_handle_t handle;
if (!args->GetNext(&handle)) {
......@@ -350,10 +366,18 @@ ZxBindings::ZxBindings(v8::Isolate* isolate, v8::Local<v8::Object> global)
gin::StringToSymbol(isolate, "$ZxObjectWaitOne"),
gin::CreateFunctionTemplate(isolate, base::BindRepeating(ZxObjectWaitOne))
->GetFunction());
global->Set(
gin::StringToSymbol(isolate, "$zx_handle_close"),
gin::CreateFunctionTemplate(isolate, base::BindRepeating(zx_handle_close))
->GetFunction());
SET_CONSTANT(ZX_HANDLE_INVALID);
SET_CONSTANT(ZX_TIME_INFINITE);
// Channel APIs.
global->Set(gin::StringToSymbol(isolate, "$ZxChannelCreate"),
gin::CreateFunctionTemplate(isolate,
base::BindRepeating(&ZxChannelCreate))
->GetFunction());
global->Set(
gin::StringToSymbol(isolate, "$ZxChannelWrite"),
gin::CreateFunctionTemplate(isolate, base::BindRepeating(&ZxChannelWrite))
......
......@@ -197,6 +197,23 @@ class BindingsSetupHelper {
DISALLOW_COPY_AND_ASSIGN(BindingsSetupHelper);
};
class AnotherInterfaceImpl : public fidljstest::AnotherInterface {
public:
AnotherInterfaceImpl(
fidl::InterfaceRequest<fidljstest::AnotherInterface> request)
: binding_(this, std::move(request)) {}
~AnotherInterfaceImpl() override = default;
void TimesTwo(int32_t a, TimesTwoCallback callback) override {
callback(a * 2);
}
private:
fidl::Binding<fidljstest::AnotherInterface> binding_;
DISALLOW_COPY_AND_ASSIGN(AnotherInterfaceImpl);
};
class TestolaImpl : public fidljstest::Testola {
public:
TestolaImpl() {
......@@ -493,6 +510,12 @@ class TestolaImpl : public fidljstest::Testola {
response_callbacks_.clear();
}
void GetAnother(
fidl::InterfaceRequest<fidljstest::AnotherInterface> request) override {
another_interface_impl_ =
std::make_unique<AnotherInterfaceImpl>(std::move(request));
}
private:
bool was_do_something_called_ = false;
int32_t received_int_ = -1;
......@@ -505,6 +528,7 @@ class TestolaImpl : public fidljstest::Testola {
zx_handle_t unowned_log_handle_;
bool did_receive_union_ = false;
bool did_get_vectors_of_string_ = false;
std::unique_ptr<AnotherInterfaceImpl> another_interface_impl_;
DISALLOW_COPY_AND_ASSIGN(TestolaImpl);
};
......@@ -1269,6 +1293,40 @@ TEST_F(FidlGenJsTest, VectorOfHandle) {
EXPECT_EQ(zx_handle_close(result_vmo1), ZX_OK);
}
TEST_F(FidlGenJsTest, RequestInterface) {
v8::Isolate* isolate = instance_->isolate();
BindingsSetupHelper helper(isolate);
TestolaImpl testola_impl;
fidl::Binding<fidljstest::Testola> binding(&testola_impl);
binding.Bind(std::move(helper.server()));
std::string source = R"(
var proxy = new TestolaProxy();
proxy.$bind(testHandle);
var another_proxy = new AnotherInterfaceProxy();
proxy.GetAnother(another_proxy.$request());
this.is_bound = another_proxy.$is_bound();
another_proxy.TimesTwo(456).then(resp => {
this.result = resp;
// TODO(crbug.com/883496): Handle created by $request() must be manually
// closed for now to avoid leaking it.
another_proxy.$close();
}).catch((e) => log('FAILED: ' + e));
// Use the original interface to make sure we didn't break its connection.
proxy.PrintInt(789);
)";
helper.runner().Run(source, "test.js");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(helper.Get<int>("result"), 456 * 2);
EXPECT_EQ(testola_impl.received_int(), 789);
}
int main(int argc, char** argv) {
base::TestSuite test_suite(argc, argv);
......
......@@ -100,6 +100,10 @@ struct AfterPreviousReference {
int32 an_int;
};
interface AnotherInterface {
1: TimesTwo(int32 a) -> (int32 b);
};
interface Testola {
1: DoSomething();
......@@ -133,4 +137,6 @@ interface Testola {
14: PassVectorOfVMO(VectorOfHandleToVMO input)
-> (VectorOfHandleToVMO output);
15: GetAnother(request<AnotherInterface> another);
};
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