summaryrefslogtreecommitdiff
path: root/libdeno/binding.cc
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2018-09-27 17:33:10 -0400
committerGitHub <noreply@github.com>2018-09-27 17:33:10 -0400
commitd38ccfc6dcb8643daa4f9e695d47a79cf068f90e (patch)
treed36ad2934e8550242d50e866f4ad2b6c303646b7 /libdeno/binding.cc
parentbf93ca54dd85686c7b93a6189913e48e10de8dcf (diff)
Support zero-copy data in libdeno.send(). (#838)
This is a large API refactor of deno.h which replaces deno_send() and deno_set_response() with deno_respond(). It also adds a req_id parameter to the deno_recv_cb. Make writeFile/writeFileSync use it.
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r--libdeno/binding.cc93
1 files changed, 76 insertions, 17 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc
index b3c59c3ec..a790573a7 100644
--- a/libdeno/binding.cc
+++ b/libdeno/binding.cc
@@ -25,6 +25,36 @@ Deno* FromIsolate(v8::Isolate* isolate) {
return static_cast<Deno*>(isolate->GetData(0));
}
+void LazilyCreateDataMap(Deno* d) {
+ if (d->async_data_map.IsEmpty()) {
+ v8::HandleScope handle_scope(d->isolate);
+ // It's important for security reasons that async_data_map is not exposed to
+ // the VM.
+ auto async_data_map = v8::Map::New(d->isolate);
+ d->async_data_map.Reset(d->isolate, async_data_map);
+ }
+ DCHECK(!d->async_data_map.IsEmpty());
+}
+
+void AddDataRef(Deno* d, int32_t req_id, v8::Local<v8::Value> data_v) {
+ LazilyCreateDataMap(d);
+ auto async_data_map = d->async_data_map.Get(d->isolate);
+ auto context = d->context.Get(d->isolate);
+ auto req_id_v = v8::Integer::New(d->isolate, req_id);
+ auto r = async_data_map->Set(context, req_id_v, data_v);
+ CHECK(!r.IsEmpty());
+}
+
+void DeleteDataRef(Deno* d, int32_t req_id) {
+ LazilyCreateDataMap(d);
+ auto context = d->context.Get(d->isolate);
+ // Delete persistent reference to data ArrayBuffer.
+ auto async_data_map = d->async_data_map.Get(d->isolate);
+ auto req_id_v = v8::Integer::New(d->isolate, req_id);
+ auto maybe_deleted = async_data_map->Delete(context, req_id_v);
+ DCHECK(maybe_deleted.IsJust());
+}
+
// Extracts a C string from a v8::V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
@@ -214,17 +244,40 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Locker locker(d->isolate);
v8::EscapableHandleScope handle_scope(isolate);
- CHECK_EQ(args.Length(), 1);
- v8::Local<v8::Value> ab_v = args[0];
- CHECK(ab_v->IsArrayBufferView());
- auto buf = GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(ab_v));
+ CHECK_EQ(d->currentArgs, nullptr); // libdeno.send re-entry forbidden.
+ int32_t req_id = d->next_req_id++;
+
+ v8::Local<v8::Value> control_v = args[0];
+ CHECK(control_v->IsArrayBufferView());
+ deno_buf control =
+ GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(control_v));
+ deno_buf data = {nullptr, 0u, nullptr, 0u};
+ v8::Local<v8::Value> data_v;
+ if (args.Length() == 2) {
+ if (args[1]->IsArrayBufferView()) {
+ data_v = args[1];
+ data = GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(data_v));
+ }
+ } else {
+ CHECK_EQ(args.Length(), 1);
+ }
DCHECK_EQ(d->currentArgs, nullptr);
d->currentArgs = &args;
- d->cb(d, buf);
+ d->cb(d, req_id, control, data);
- d->currentArgs = nullptr;
+ if (d->currentArgs == nullptr) {
+ // This indicates that deno_repond() was called already.
+ } else {
+ // Asynchronous.
+ d->currentArgs = nullptr;
+ // If the data ArrayBuffer was given, we must maintain a strong reference
+ // to it until deno_respond is called.
+ if (!data_v.IsEmpty()) {
+ AddDataRef(d, req_id, data_v);
+ }
+ }
}
// Sets the global error handler.
@@ -358,6 +411,7 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
}
void AddIsolate(Deno* d, v8::Isolate* isolate) {
+ d->next_req_id = 0;
d->isolate = isolate;
// Leaving this code here because it will probably be useful later on, but
// disabling it now as I haven't got tests for the desired behavior.
@@ -400,7 +454,17 @@ int deno_execute(Deno* d, const char* js_filename, const char* js_source) {
return deno::Execute(context, js_filename, js_source) ? 1 : 0;
}
-int deno_send(Deno* d, deno_buf buf) {
+int deno_respond(Deno* d, int32_t req_id, deno_buf buf) {
+ if (d->currentArgs != nullptr) {
+ // Synchronous response.
+ auto ab = deno::ImportBuf(d->isolate, buf);
+ d->currentArgs->GetReturnValue().Set(ab);
+ d->currentArgs = nullptr;
+ return 0;
+ }
+
+ // Asynchronous response.
+
v8::Locker locker(d->isolate);
v8::Isolate::Scope isolate_scope(d->isolate);
v8::HandleScope handle_scope(d->isolate);
@@ -410,10 +474,12 @@ int deno_send(Deno* d, deno_buf buf) {
v8::TryCatch try_catch(d->isolate);
+ deno::DeleteDataRef(d, req_id);
+
auto recv = d->recv.Get(d->isolate);
if (recv.IsEmpty()) {
d->last_exception = "libdeno.recv has not been called.";
- return 0;
+ return 1;
}
v8::Local<v8::Value> args[1];
@@ -422,17 +488,10 @@ int deno_send(Deno* d, deno_buf buf) {
if (try_catch.HasCaught()) {
deno::HandleException(context, try_catch.Exception());
- return 0;
+ return 1;
}
- return 1;
-}
-
-void deno_set_response(Deno* d, deno_buf buf) {
- // printf("deno_set_response: ");
- // hexdump(buf.data_ptr, buf.data_len);
- auto ab = deno::ImportBuf(d->isolate, buf);
- d->currentArgs->GetReturnValue().Set(ab);
+ return 0;
}
void deno_delete(Deno* d) {