39 Commits
Author SHA1 Message Date
Kenton Varda a2ed298d34 Merge pull request #68 from zenhack/module-stubs
Add .capnp.js stubs generated by capnpc-node-typescript.
2020-12-16 21:52:36 -06:00
Kenton Varda 8509161f7c Merge pull request #67 from zenhack/typescript-decl-fix
Small fix in the typescript declarations, and add some comments.
2020-12-14 16:53:50 -06:00
Ian Denhardt b88ab5a305 Add .capnp.js stubs generated by capnpc-node-typescript.
Per discussion in #62, this allows importing modules to use "normal"
imports, which is important for typescript since it doesn't understand
`capnp.importSystem()` and friends.

We could also merge in the type declarations themselves, but that isn't
strictly necessary, since typescript is built around being able to
supply your own stubs for other libraries. I may do something in that
vein in a follow-on pr, but I want to think a bit about how this
will all fit together.
2020-12-12 02:29:24 -05:00
Ian Denhardt ac4c2574a6 ts declarations: Add comments describing how the type parameters work. 2020-12-12 02:00:08 -05:00
Ian Denhardt 0ba69aec95 Fix a mistake in the type declaration file.
The argument to serialize() should be a builder, not a reader.
2020-12-12 01:54:15 -05:00
Kenton Varda 7ef8da3d79 Merge pull request #66 from zenhack/gitignore-capnp.node
Add capnp.node to .gitignore
2020-12-07 13:39:16 -06:00
Ian Denhardt 0e9376071e Add capnp.node to .gitignore 2020-12-07 10:42:43 -05:00
Kenton Varda d75642d0e8 Merge pull request #62 from zenhack/typescript-declaration-file
Add a typescript declaration file.
2020-11-30 16:05:15 -06:00
Ian Denhardt 6eb94df293 Add a typescript declaration file.
This covers most of the API that is documented in the README, though I
see that there are some things exposed in `capnp.js` but not documented
-- the declaration file does not currently cover those.

This is designed to work with the output of:

    https://github.com/zenhack/capnpc-node-typescript

...which is nearing completion.
2020-10-28 16:30:22 -04:00
Kenton Varda 544bf09271 Merge pull request #60 from ocdtrekkie/patch-1
Readme typos
2020-06-08 09:53:11 -05:00
Jacob Weisz b45f75c8f9 Readme typos 2020-06-08 07:38:12 -05:00
Kenton Varda 752d3cdffd Merge pull request #59 from zenhack/null-handle-cleanup
More defensive/sensible handling of a case that probably can't happen.
2020-05-11 10:03:05 -05:00
Ian Denhardt 0371c68bcd More defensive/sensible handling of a case that probably can't happen.
I found this by running clang-analyzer. With the previous version of the
code, if `handler` is null, then we assign `self = nullptr` and then
immediately call a method on it.

I'm pretty sure this can't actually happen -- I don't think
`uv_timer_start` will ever pass nullptr to the callback -- but the logic
is obviously bogus so let's replace it with something defensive.
2020-05-09 23:56:19 -04:00
Kenton Varda ae6f591602 Merge pull request #58 from zenhack/version-bump
Bump version in package.json
2020-03-24 13:03:16 -05:00
Ian Denhardt 81aeebf181 Bump version in package.json
I just published this to the npm registry, we should probably update the
version in the git repo too.

It seems to work on node 12, though node 10 is still failing. I actually
*can* reproduce the failures locally if:

- I start a fresh npm project and do `npm install
  `/path/to/node-canpnp/src`
- The working directory for `node-capnp` is clean.

I may try to troubleshoot this more later. Perhaps we should unpublish,
but having a version that works on node 12 is possibly an improvement,
so maybe not...

Npm insisted that 0.4.0 already existed and was refusing to re-publish
it (I assume there was a deleted failed attempt), so I had to bump the
version to get it to work.
2020-03-23 16:01:04 -04:00
Kenton Varda 0cc5054ea2 Merge branch 'node8' into node10 2020-03-14 18:24:46 -05:00
Kenton Varda cdda439e4a Merge pull request #57 from redsift/node12
Node12 support
2020-03-14 17:25:45 -05:00
Kenton Varda 932e589f36 Merge pull request #55 from zenhack/exception-docs
Document how exceptions work.
2020-01-08 17:50:15 -06:00
Ian Denhardt 46fd0a8b6e Document how exceptions work.
Fixes #53.
2020-01-08 17:53:10 -05:00
Kenton Varda 5e8c4c0b1b Merge pull request #51 from redsift/fix/build
Fix the build process.
2020-01-07 10:56:44 -06:00
Deepak Prabhakara 7ab91af445 pipe stdout and stderr from child process so we can see the logs 2019-12-22 23:32:31 +00:00
Deepak Prabhakara 05644ec31b v8 and nan compilation fixes for node12 2019-12-22 23:26:49 +00:00
Christos Vontas 28de5052c0 Merge pull request #1 from redsift/fix/build
afterBuild needs to run after all IO has finished
2019-12-11 13:50:24 +00:00
Christos Vontas c720781a5f afterBuild needs to run after all IO has finished 2019-12-11 13:42:05 +00:00
Kenton Varda d8de1b49ad Disambiguate getsockname() call. 2019-11-23 11:19:17 -08:00
Kenton Varda 4df200effa Implement KJ's new whenWriteDisconnected(). 2019-02-09 15:31:44 -08:00
Kenton Varda cbb3ab40fb Fix age-old bug with flag combining.
I guess it never cauased a problem because the result was 1 which just happened to map to TAKE_OWNERSHIP and the other flags are safe to skip (just inefficient)...
2019-02-09 15:29:42 -08:00
Kenton Varda eae9c1c54b Update for Node 10. 2019-01-07 13:44:26 -08:00
Kenton Varda 1abd120c12 Update to C++14 as required by Cap'n Proto 0.7. 2019-01-07 13:40:56 -08:00
Kenton Varda ca30a96c1c Merge pull request #43 from qm3ster/patch-1
update capnp-js link
2018-05-14 15:27:29 -07:00
Mihal Malostanidis d2f4c72d64 update capnp-js link
Reorganization in progress
2018-05-08 00:02:48 +03:00
Kenton Varda 202e13c66f Hack: For Sandstorm build, catch segfaults and print stack traces. 2018-04-15 17:28:00 -07:00
Kenton Varda a3e975d43c Bump version number and publish again because apparently npm went and published a 13MB scratch folder I had in the directory last time, wtf. 2018-02-09 08:43:47 -08:00
Kenton Varda f1e9306cd6 Release 0.3.0 which supports node 8+. 2018-02-04 14:17:42 -08:00
Kenton Varda c6fe3c2624 Revert "Add temporary code to count local capability types and support dumping them to stderr periodically."
This reverts commit 74958750f1.

This is no longer needed.
2018-02-04 14:16:01 -08:00
Kenton Varda 7fb9018515 Correctly handle pipelining on a Capability. 2018-02-04 14:12:01 -08:00
Kenton Varda 742d6b5b79 Fix obvious, glaring memory leak. I dunno what to say. 2018-01-06 12:17:56 -08:00
Kenton Varda ec1721f298 Node 8: Properly support capabilities defined using ES6 classes.
I'm not sure why this worked with Node 4 / Meteor 1.5, but behavior changed in Node 8 / Meteor 1.6.
2017-11-18 16:50:37 -08:00
Kenton Varda 5aeffa328a Node 8: Transition from V8 hidden values to V8 private values. 2017-11-18 16:49:55 -08:00
15 changed files with 376 additions and 224 deletions

No files matched your search

+38 -3
View File
@@ -22,7 +22,7 @@ messages from complete Javascript objects. This actually makes the library
somewhat nicer syntactically than it would be otherwise, but it is not fast. somewhat nicer syntactically than it would be otherwise, but it is not fast.
A pure-Javascript implementation would likely be much faster. See A pure-Javascript implementation would likely be much faster. See
[capnp-js](https://github.com/capnp-js/serialization) for such an [capnp-js](https://github.com/capnp-js) for such an
implementation. Unfortunately, that implementation is incomplete and does not implementation. Unfortunately, that implementation is incomplete and does not
support [RPC](https://github.com/capnp-js/rpc) just yet. Hence, this hack was support [RPC](https://github.com/capnp-js/rpc) just yet. Hence, this hack was
created for short-term use. created for short-term use.
@@ -49,9 +49,9 @@ On Debian/Ubuntu, you can install these like so:
sudo apt-get install nodejs-dev nodejs-legacy capnproto-dev g++ sudo apt-get install nodejs-dev nodejs-legacy capnproto-dev g++
### Form source ### From source
git clone git://github.com/kenton/node-capnp.git git clone git://github.com/capnproto/node-capnp.git
cd node-capnp cd node-capnp
npm install npm install
@@ -118,6 +118,25 @@ we copied.)
// object containing the results by name. // object containing the results by name.
promise.then(function(response) { promise.then(function(response) {
console.log(response.namedResult); console.log(response.namedResult);
})
// If the remote object throws an rpc exception, the promise will be
// rejected. The error returned will have a property `kjType`, which
// will be a string representation of the exception's `type` field.
.catch(function(error) {
switch(error.kjType) {
case 'failed':
console.log("A generic problem occurred:", error.message);
break;
case 'overloaded':
console.log("Resource overload on the remote end:", error.message);
break;
case 'disconnected':
// ...
break;
case 'unimplemented':
// ...
break;
}
}); });
### Pipelining ### Pipelining
@@ -196,6 +215,22 @@ then use that:
In this case, the library only wraps `myFoo` as a capability once, and then In this case, the library only wraps `myFoo` as a capability once, and then
calls `close()` once all copies of that reference have been dropped. calls `close()` once all copies of that reference have been dropped.
If one of your methods throws an exception, or returns a promise which
is subsequently rejected, it will be converted to a capnproto rpc
exception. If the exception has a `kjType` property, that will be used
for the exception's `type` field, otherwise the type will be `failed`.
var myBar = {
bar: function(foo) {
if(!bazAvailable()) {
var err = new Error("The baz is busy; try again later.");
err.kjType = 'overloaded';
throw(err);
}
// ...
}
}
### Exporting a bootstrap capability ### Exporting a bootstrap capability
// Connect to another server and export `myFoo` as a bootstrap capability. // Connect to another server and export `myFoo` as a bootstrap capability.
+1
View File
@@ -0,0 +1 @@
capnp.node
+2 -2
View File
@@ -4,12 +4,12 @@
'target_name': 'capnp', 'target_name': 'capnp',
'sources': ['src/node-capnp/capnp.cc'], 'sources': ['src/node-capnp/capnp.cc'],
'libraries': ['-lkj', '-lkj-async', '-lcapnp', '-lcapnpc', '-lcapnp-rpc'], 'libraries': ['-lkj', '-lkj-async', '-lcapnp', '-lcapnpc', '-lcapnp-rpc'],
'cflags_cc': ['-std=c++11'], 'cflags_cc': ['-std=c++14'],
'cflags_cc!': ['-fno-rtti', '-fno-exceptions'], 'cflags_cc!': ['-fno-rtti', '-fno-exceptions'],
'conditions': [ 'conditions': [
[ 'OS=="mac"', { [ 'OS=="mac"', {
'xcode_settings': { 'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'], 'OTHER_CPLUSPLUSFLAGS' : ['-std=c++14','-stdlib=libc++'],
'OTHER_LDFLAGS': ['-stdlib=libc++'], 'OTHER_LDFLAGS': ['-stdlib=libc++'],
'GCC_ENABLE_CPP_RTTI': 'YES', 'GCC_ENABLE_CPP_RTTI': 'YES',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
+7 -3
View File
@@ -72,10 +72,13 @@ if (!force) {
// Build it // Build it
function build() { function build() {
cp.spawn( var sp = cp.spawn(
process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp', process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp',
['rebuild'].concat(args), ['rebuild'].concat(args),
{customFds: [0, 1, 2]}) {customFds: [0, 1, 2]});
sp
.on('close', function(){ afterBuild(); })
.on('exit', function(err) { .on('exit', function(err) {
if (err) { if (err) {
if (err === 127) { if (err === 127) {
@@ -88,8 +91,9 @@ function build() {
} }
return process.exit(err); return process.exit(err);
} }
afterBuild();
}); });
sp.stdout.pipe(process.stdout);
sp.stderr.pipe(process.stderr);
} }
// Move it to expected location // Move it to expected location
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/c++.capnp");
+7 -4
View File
@@ -1,8 +1,11 @@
{ {
"name": "capnp", "name": "capnp",
"version": "0.2.0", "version": "0.5.0",
"description": "A wrapper for the C++ Cap'n Proto library", "description": "A wrapper for the C++ Cap'n Proto library",
"keywords": [ "capnproto", "serialization" ], "keywords": [
"capnproto",
"serialization"
],
"homepage": "http://capnproto.org", "homepage": "http://capnproto.org",
"author": { "author": {
"name": "Kenton Varda", "name": "Kenton Varda",
@@ -15,8 +18,8 @@
"email": "jparyani@gmail.com" "email": "jparyani@gmail.com"
} }
], ],
"main": "src/node-capnp/capnp.js", "main": "src/node-capnp/capnp.js",
"types": "src/node-capnp/capnp.d.ts",
"scripts": { "scripts": {
"install": "node ./build.js", "install": "node ./build.js",
"test": "node src/node-capnp/capnp-test.js" "test": "node src/node-capnp/capnp-test.js"
@@ -26,7 +29,7 @@
"url": "git://github.com/kentonv/node-capnp.git" "url": "git://github.com/kentonv/node-capnp.git"
}, },
"engines": { "engines": {
"node": ">=0.12" "node": ">=10.0"
}, },
"license": "MIT" "license": "MIT"
} }
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/persistent.capnp");
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/rpc-twoparty.capnp");
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/rpc.capnp");
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/schema.capnp");
+38 -21
View File
@@ -263,31 +263,48 @@ child.stdio[1].once("readable", function() {
closed: false closed: false
}; };
var localCap = new capnp.Capability(pow, Calculator.Function); function usePow(pow) {
assert.equal(9, wait(localCap.call([3, 2])).value); var localCap = new capnp.Capability(pow, Calculator.Function);
assert(!pow.closed); assert.equal(9, wait(localCap.call([3, 2])).value);
localCap.close(); assert(!pow.closed);
assert(pow.closed); localCap.close();
assert(pow.closed);
var promise = calc.evaluate( var promise = calc.evaluate(
{call: {"function": subtract, params: [ {call: {"function": subtract, params: [
{call: {"function": add, params: [ {call: {"function": add, params: [
{literal: 123}, {literal: 456}]}}, {literal: 123}, {literal: 456}]}},
{literal: 321}]}}); {literal: 321}]}});
var value = promise.value; var value = promise.value;
assert.equal(258, wait(value.read()).value); assert.equal(258, wait(value.read()).value);
value.close(); value.close();
pow.closed = false; pow.closed = false;
value = calc.evaluate( value = calc.evaluate(
{call: {"function": pow, params: [{literal: 2}, {literal: 4}]}}).value; {call: {"function": pow, params: [{literal: 2}, {literal: 4}]}}).value;
assert.equal(16, wait(value.read()).value); assert.equal(16, wait(value.read()).value);
// Wait a moment to give the capability a chance to be dropped. // Wait a moment to give the capability a chance to be dropped.
wait(new Promise((resolve, reject) => setTimeout(resolve, 10))); wait(new Promise((resolve, reject) => setTimeout(resolve, 10)));
assert(pow.closed); assert(pow.closed);
value.close(); value.close();
}
usePow(pow);
// Try using a class.
class PowClass {
constructor() {
this.closed = false
}
call(params) {
return Math.pow(params[0], params[1]);
}
close() {
this.closed = true;
}
};
usePow(new PowClass());
// Try wrapping a promise as a capability -- calls are queued until resolution. // Try wrapping a promise as a capability -- calls are queued until resolution.
var resolvePromisedCalc; var resolvePromisedCalc;
+219 -180
View File
@@ -47,7 +47,6 @@
#include <set> #include <set>
#include <stdlib.h> #include <stdlib.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <algorithm>
#include <typeinfo> #include <typeinfo>
#include <typeindex> #include <typeindex>
@@ -195,7 +194,8 @@ private:
} }
static void doRun(uv_timer_t* handle) { static void doRun(uv_timer_t* handle) {
UvEventPort* self = handle == nullptr ? nullptr : reinterpret_cast<UvEventPort*>(handle->data); KJ_ASSERT(handle != nullptr);
UvEventPort* self = reinterpret_cast<UvEventPort*>(handle->data);
self->run(); self->run();
} }
}; };
@@ -237,7 +237,7 @@ static int applyFlags(int fd, uint flags) {
static constexpr uint NEW_FD_FLAGS = static constexpr uint NEW_FD_FLAGS =
#if __linux__ #if __linux__
kj::LowLevelAsyncIoProvider::ALREADY_CLOEXEC || kj::LowLevelAsyncIoProvider::ALREADY_NONBLOCK || kj::LowLevelAsyncIoProvider::ALREADY_CLOEXEC | kj::LowLevelAsyncIoProvider::ALREADY_NONBLOCK |
#endif #endif
kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP; kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP;
// We always try to open FDs with CLOEXEC and NONBLOCK already set on Linux, but on other platforms // We always try to open FDs with CLOEXEC and NONBLOCK already set on Linux, but on other platforms
@@ -410,6 +410,13 @@ public:
KJ_SYSCALL(shutdown(fd, SHUT_WR)); KJ_SYSCALL(shutdown(fd, SHUT_WR));
} }
#if CAPNP_VERSION >= 8000
kj::Promise<void> whenWriteDisconnected() override {
// TODO(someday): Implement using UV_DISCONNECT?
return kj::NEVER_DONE;
}
#endif
private: private:
kj::Promise<size_t> tryReadInternal(void* buffer, size_t minBytes, size_t maxBytes, kj::Promise<size_t> tryReadInternal(void* buffer, size_t minBytes, size_t maxBytes,
size_t alreadyRead) { size_t alreadyRead) {
@@ -564,7 +571,7 @@ public:
struct sockaddr_in6 inet6; struct sockaddr_in6 inet6;
} addr; } addr;
addrlen = sizeof(addr); addrlen = sizeof(addr);
KJ_SYSCALL(getsockname(fd, &addr.generic, &addrlen)); KJ_SYSCALL(::getsockname(fd, &addr.generic, &addrlen));
switch (addr.generic.sa_family) { switch (addr.generic.sa_family) {
case AF_INET: return ntohs(addr.inet4.sin_port); case AF_INET: return ntohs(addr.inet4.sin_port);
case AF_INET6: return ntohs(addr.inet6.sin6_port); case AF_INET6: return ntohs(addr.inet6.sin6_port);
@@ -690,12 +697,12 @@ class OwnHandle {
public: public:
OwnHandle() = default; OwnHandle() = default;
KJ_DISALLOW_COPY(OwnHandle); KJ_DISALLOW_COPY(OwnHandle);
inline OwnHandle(const v8::Handle<T>& other) inline OwnHandle(const v8::Local<T>& other)
: handle(v8::Isolate::GetCurrent(), other) {} : handle(v8::Isolate::GetCurrent(), other) {}
inline OwnHandle(OwnHandle&& other) = default; inline OwnHandle(OwnHandle&& other) = default;
inline OwnHandle& operator=(OwnHandle&& other) = default; inline OwnHandle& operator=(OwnHandle&& other) = default;
inline OwnHandle& operator=(const v8::Handle<T>& other) { inline OwnHandle& operator=(const v8::Local<T>& other) {
handle.Reset(v8::Isolate::GetCurrent(), other); handle.Reset(v8::Isolate::GetCurrent(), other);
return *this; return *this;
} }
@@ -704,7 +711,7 @@ public:
inline bool operator!=(decltype(nullptr)) { return !handle.IsEmpty(); } inline bool operator!=(decltype(nullptr)) { return !handle.IsEmpty(); }
inline T* operator->() const { return get().operator->(); } inline T* operator->() const { return get().operator->(); }
inline const v8::Handle<T> get() const { inline const v8::Local<T> get() const {
return v8::Local<T>::New(v8::Isolate::GetCurrent(), handle); return v8::Local<T>::New(v8::Isolate::GetCurrent(), handle);
} }
@@ -712,16 +719,16 @@ private:
v8::Global<T> handle; v8::Global<T> handle;
}; };
kj::String toKjString(v8::Handle<v8::String> handle) { kj::String toKjString(v8::Local<v8::String> handle) {
auto buf = kj::heapArray<char>(handle->Utf8Length() + 1); auto buf = kj::heapArray<char>(handle->Utf8Length(v8::Isolate::GetCurrent()) + 1);
handle->WriteUtf8(buf.begin(), buf.size()); handle->WriteUtf8(v8::Isolate::GetCurrent(), buf.begin(), buf.size());
buf[buf.size() - 1] = 0; buf[buf.size() - 1] = 0;
return kj::String(kj::mv(buf)); return kj::String(kj::mv(buf));
} }
kj::String toKjString(v8::Handle<v8::Value> handle) { kj::String toKjString(v8::Local<v8::Value> handle) {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
return toKjString(handle->ToString()); return toKjString(handle->ToString(v8::Isolate::GetCurrent()));
} }
#define KJV8_STACK_STR(name, handle, sizeHint) \ #define KJV8_STACK_STR(name, handle, sizeHint) \
@@ -729,16 +736,16 @@ kj::String toKjString(v8::Handle<v8::Value> handle) {
kj::Array<char> name##_heap; \ kj::Array<char> name##_heap; \
kj::StringPtr name; \ kj::StringPtr name; \
{ \ { \
v8::Handle<v8::String> v8str = handle->ToString(); \ v8::Local<v8::String> v8str = handle->ToString(v8::Isolate::GetCurrent()); \
char* ptr; \ char* ptr; \
size_t len = v8str->Utf8Length(); \ size_t len = v8str->Utf8Length(v8::Isolate::GetCurrent()); \
if (len < sizeHint) { \ if (len < sizeHint) { \
ptr = name##_buf; \ ptr = name##_buf; \
} else { \ } else { \
name##_heap = kj::heapArray<char>(len + 1); \ name##_heap = kj::heapArray<char>(len + 1); \
ptr = name##_heap.begin(); \ ptr = name##_heap.begin(); \
} \ } \
v8str->WriteUtf8(ptr, len); \ v8str->WriteUtf8(v8::Isolate::GetCurrent(), ptr, len); \
ptr[len] = '\0'; \ ptr[len] = '\0'; \
name = kj::StringPtr(ptr, len); \ name = kj::StringPtr(ptr, len); \
} }
@@ -768,14 +775,14 @@ v8::Local<v8::Value> toJsException(kj::Exception&& exception) {
return result; return result;
} }
kj::Exception fromJsException(v8::Handle<v8::Value> exception) { kj::Exception fromJsException(v8::Local<v8::Value> exception) {
kj::Exception::Type type = kj::Exception::Type::FAILED; kj::Exception::Type type = kj::Exception::Type::FAILED;
kj::String description; kj::String description;
if (exception->IsObject()) { if (exception->IsObject()) {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Object* obj = v8::Object::Cast(*exception); v8::Object* obj = v8::Object::Cast(*exception);
v8::Handle<v8::Value> value = obj->Get(newSymbol("kjType")); v8::Local<v8::Value> value = obj->Get(newSymbol("kjType"));
if (!value.IsEmpty() && !value->IsUndefined()) { if (!value.IsEmpty() && !value->IsUndefined()) {
auto name = toKjString(value); auto name = toKjString(value);
if (name == "overloaded") { if (name == "overloaded") {
@@ -787,7 +794,7 @@ kj::Exception fromJsException(v8::Handle<v8::Value> exception) {
} }
} }
v8::Handle<v8::Value> stack = obj->Get(newSymbol("stack")); v8::Local<v8::Value> stack = obj->Get(newSymbol("stack"));
if (!stack.IsEmpty() && !stack->IsUndefined()) { if (!stack.IsEmpty() && !stack->IsUndefined()) {
description = toKjString(stack); description = toKjString(stack);
} else { } else {
@@ -866,6 +873,17 @@ class Wrapper {
handle.SetWeak(this, &deleteCallback, v8::WeakCallbackType::kParameter); handle.SetWeak(this, &deleteCallback, v8::WeakCallbackType::kParameter);
} }
~WrappedObject() {
// Since destructors might perform non-trivial operations, including calling back into
// JavaScript, delay the destructor until the next turn of the event loop.
T* ptrCopy = ptr;
kj::evalLater([ptrCopy]() {
delete ptrCopy;
}).detach([](kj::Exception&& exception) {
KJ_LOG(ERROR, exception);
});
}
static void deleteCallback(const v8::WeakCallbackInfo<WrappedObject>& data) { static void deleteCallback(const v8::WeakCallbackInfo<WrappedObject>& data) {
delete reinterpret_cast<WrappedObject*>(data.GetParameter()); delete reinterpret_cast<WrappedObject*>(data.GetParameter());
} }
@@ -874,7 +892,9 @@ class Wrapper {
public: public:
template <typename T> template <typename T>
v8::Local<v8::Object> wrap(T* ptr) { v8::Local<v8::Object> wrap(T* ptr) {
v8::Local<v8::Object> obj = getFunctionTemplate<T>()->GetFunction()->NewInstance(); v8::Local<v8::Object> obj = getFunctionTemplate<T>()
->GetFunction(v8::Isolate::GetCurrent()->GetCurrentContext()).ToLocalChecked()->NewInstance(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToLocalChecked();
obj->SetAlignedPointerInInternalField(0, new WrappedObject<T>(obj, ptr)); obj->SetAlignedPointerInInternalField(0, new WrappedObject<T>(obj, ptr));
return obj; return obj;
} }
@@ -885,24 +905,27 @@ public:
} }
template <typename T> template <typename T>
static kj::Maybe<T&> tryUnwrap(v8::Handle<v8::Value> hdl) { static kj::Maybe<T&> tryUnwrap(v8::Local<v8::Value> hdl) {
if (!hdl->IsObject()) return nullptr; if (!hdl->IsObject()) return nullptr;
v8::Handle<v8::Object> obj(v8::Handle<v8::Object>::Cast(hdl)); v8::Local<v8::Object> obj(v8::Local<v8::Object>::Cast(hdl));
if (getFunctionTemplate<T>()->HasInstance(obj)) { if (getFunctionTemplate<T>()->HasInstance(obj)) {
return *reinterpret_cast<WrappedObject<T>*>(obj->GetAlignedPointerFromInternalField(0))->ptr; return *reinterpret_cast<WrappedObject<T>*>(obj->GetAlignedPointerFromInternalField(0))->ptr;
} else { } else {
v8::Handle<v8::Value> native = obj->GetHiddenValue(newSymbol("capnp::native")); v8::Isolate* isolate = v8::Isolate::GetCurrent();
if (native.IsEmpty() || native->IsUndefined()) { v8::Local<v8::Value> native;
return nullptr; if (obj->GetPrivate(isolate->GetCurrentContext(),
} else { v8::Private::ForApi(isolate, newSymbol("capnp::native"))).ToLocal(&native) &&
!native->IsUndefined()) {
return tryUnwrap<T>(native); return tryUnwrap<T>(native);
} else {
return nullptr;
} }
} }
} }
template <typename T> template <typename T>
static kj::Maybe<T&> unwrap(v8::Handle<v8::Value> hdl) { static kj::Maybe<T&> unwrap(v8::Local<v8::Value> hdl) {
KJ_IF_MAYBE(result, tryUnwrap<T>(hdl)) { KJ_IF_MAYBE(result, tryUnwrap<T>(hdl)) {
return *result; return *result;
} else { } else {
@@ -919,7 +942,7 @@ private:
} }
template <typename T> template <typename T>
static v8::Handle<v8::FunctionTemplate> getFunctionTemplate() { static v8::Local<v8::FunctionTemplate> getFunctionTemplate() {
// Get the singleton FunctionTemplate for some wrapped type T. // Get the singleton FunctionTemplate for some wrapped type T.
// We allocate on the heap in order to prevent the destructor from running on shutdown, when // We allocate on the heap in order to prevent the destructor from running on shutdown, when
@@ -931,9 +954,9 @@ private:
} }
template <typename T> template <typename T>
static v8::Handle<v8::FunctionTemplate> newFunctionTemplate() { static v8::Local<v8::FunctionTemplate> newFunctionTemplate() {
const std::type_info& type = typeid(T); const std::type_info& type = typeid(T);
v8::Handle<v8::FunctionTemplate> result = v8::FunctionTemplate::New(v8::Isolate::GetCurrent()); v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(v8::Isolate::GetCurrent());
result->InstanceTemplate()->SetInternalFieldCount(1); result->InstanceTemplate()->SetInternalFieldCount(1);
// TODO(someday): Make stuff work with -fno-rtti? node itself is compiled without RTTI... // TODO(someday): Make stuff work with -fno-rtti? node itself is compiled without RTTI...
@@ -951,7 +974,7 @@ private:
if (name##_maybe == nullptr) KJV8_TYPE_ERROR(name, type); \ if (name##_maybe == nullptr) KJV8_TYPE_ERROR(name, type); \
type& name KJ_UNUSED = KJ_ASSERT_NONNULL(name##_maybe) type& name KJ_UNUSED = KJ_ASSERT_NONNULL(name##_maybe)
kj::Maybe<kj::ArrayPtr<const byte>> unwrapBuffer(v8::Handle<v8::Value> value) { kj::Maybe<kj::ArrayPtr<const byte>> unwrapBuffer(v8::Local<v8::Value> value) {
if (!node::Buffer::HasInstance(value)) { if (!node::Buffer::HasInstance(value)) {
return nullptr; return nullptr;
} }
@@ -971,7 +994,7 @@ void deleteArray(char*, void* hint) {
} }
template <typename T> template <typename T>
v8::Handle<v8::Value> wrapBuffer(kj::Array<T>&& array) { v8::Local<v8::Value> wrapBuffer(kj::Array<T>&& array) {
char* data = reinterpret_cast<char*>(array.begin()); char* data = reinterpret_cast<char*>(array.begin());
size_t size = array.size() * sizeof(T); size_t size = array.size() * sizeof(T);
return node::Buffer::New(v8::Isolate::GetCurrent(), data, size, &deleteArray<T>, return node::Buffer::New(v8::Isolate::GetCurrent(), data, size, &deleteArray<T>,
@@ -1013,15 +1036,19 @@ void setNative(const v8::FunctionCallbackInfo<v8::Value>& args) {
// passed to fromJs(). // passed to fromJs().
if (args[0]->IsObject()) { if (args[0]->IsObject()) {
v8::Object::Cast(*args[0])->SetHiddenValue(newSymbol("capnp::native"), args[1]); v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Object::Cast(*args[0])->SetPrivate(
isolate->GetCurrentContext(),
v8::Private::ForApi(isolate, newSymbol("capnp::native")),
args[1]);
} }
} }
v8::Handle<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Reader value, v8::Local<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Reader value,
capnp::Type type, v8::Handle<v8::Value> capConstructor); capnp::Type type, v8::Local<v8::Value> capConstructor);
v8::Handle<v8::Value> schemaToObject(capnp::ParsedSchema schema, CapnpContext& context, v8::Local<v8::Value> schemaToObject(capnp::ParsedSchema schema, CapnpContext& context,
v8::Handle<v8::Value> wrappedContext) { v8::Local<v8::Value> wrappedContext) {
auto proto = schema.getProto(); auto proto = schema.getProto();
if (proto.isConst()) { if (proto.isConst()) {
auto c = schema.asConst(); auto c = schema.asConst();
@@ -1060,7 +1087,7 @@ void import(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
kj::Array<kj::String> searchPath; kj::Array<kj::String> searchPath;
kj::Array<kj::StringPtr> searchPathPtrs; kj::Array<kj::StringPtr> searchPathPtrs;
if (!args[2]->IsUndefined()) { if (!args[2]->IsUndefined()) {
@@ -1068,7 +1095,7 @@ void import(const v8::FunctionCallbackInfo<v8::Value>& args) {
searchPath = kj::heapArray<kj::String>(arr->Length()); searchPath = kj::heapArray<kj::String>(arr->Length());
searchPathPtrs = kj::heapArray<kj::StringPtr>(searchPath.size()); searchPathPtrs = kj::heapArray<kj::StringPtr>(searchPath.size());
for (uint i: kj::indices(searchPath)) { for (uint i: kj::indices(searchPath)) {
searchPath[i] = toKjString(arr->Get(i)->ToString()); searchPath[i] = toKjString(arr->Get(i)->ToString(v8::Isolate::GetCurrent()));
searchPathPtrs[i] = searchPath[i]; searchPathPtrs[i] = searchPath[i];
} }
} }
@@ -1087,7 +1114,7 @@ void import(const v8::FunctionCallbackInfo<v8::Value>& args) {
}); });
} }
void enumerateMethods(capnp::InterfaceSchema schema, v8::Handle<v8::Object> methodMap, void enumerateMethods(capnp::InterfaceSchema schema, v8::Local<v8::Object> methodMap,
CapnpContext& context, std::set<uint64_t>& seen) { CapnpContext& context, std::set<uint64_t>& seen) {
auto proto = schema.getProto(); auto proto = schema.getProto();
if (seen.insert(proto.getId()).second) { if (seen.insert(proto.getId()).second) {
@@ -1111,7 +1138,7 @@ void methods(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP(capnp::Schema, schema, args[0]); KJV8_UNWRAP(capnp::Schema, schema, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
auto proto = schema.getProto(); auto proto = schema.getProto();
KJ_REQUIRE(proto.isInterface(), "not an interface type", proto.getDisplayName()); KJ_REQUIRE(proto.isInterface(), "not an interface type", proto.getDisplayName());
@@ -1149,7 +1176,7 @@ struct ServerResults: public kj::Refcounted {
// Becomes null when call returns. // Becomes null when call returns.
}; };
kj::Maybe<capnp::DynamicStruct::Builder> unwrapBuilder(v8::Handle<v8::Value> handle) { kj::Maybe<capnp::DynamicStruct::Builder> unwrapBuilder(v8::Local<v8::Value> handle) {
// We accept either StructBuilder or Request<DynamicStruct, DynamicStruct>. // We accept either StructBuilder or Request<DynamicStruct, DynamicStruct>.
capnp::DynamicStruct::Builder builder; capnp::DynamicStruct::Builder builder;
KJ_IF_MAYBE(request, Wrapper::tryUnwrap<ClientRequest>(handle)) { KJ_IF_MAYBE(request, Wrapper::tryUnwrap<ClientRequest>(handle)) {
@@ -1220,7 +1247,7 @@ struct ServerRequest {
// Becomes non-null when getResults() is first called. Subsequent calls return the same object. // Becomes non-null when getResults() is first called. Subsequent calls return the same object.
}; };
kj::Maybe<capnp::DynamicStruct::Reader> unwrapReader(v8::Handle<v8::Value> handle) { kj::Maybe<capnp::DynamicStruct::Reader> unwrapReader(v8::Local<v8::Value> handle) {
// We accept any builder as well as Response<DynamicStruct>. // We accept any builder as well as Response<DynamicStruct>.
KJ_IF_MAYBE(response, Wrapper::tryUnwrap<ClientResponse>(handle)) { KJ_IF_MAYBE(response, Wrapper::tryUnwrap<ClientResponse>(handle)) {
return response->response.map( return response->response.map(
@@ -1256,7 +1283,7 @@ void newBuilder(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP(capnp::Schema, schema, args[0]); KJV8_UNWRAP(capnp::Schema, schema, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
KJ_REQUIRE(schema.getProto().isStruct(), KJ_REQUIRE(schema.getProto().isStruct(),
"not a struct type", schema.getProto().getDisplayName()); "not a struct type", schema.getProto().getDisplayName());
@@ -1272,7 +1299,7 @@ void copyBuilder(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP_READER(reader, args[0]); KJV8_UNWRAP_READER(reader, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return context.wrapper.wrap(new StructBuilder(reader)); return context.wrapper.wrap(new StructBuilder(reader));
}); });
} }
@@ -1285,7 +1312,7 @@ void structToString(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP_READER(reader, args[0]); KJV8_UNWRAP_READER(reader, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return newString(kj::str(reader.getSchema().getProto().getDisplayName(), reader).cStr()); return newString(kj::str(reader.getSchema().getProto().getDisplayName(), reader).cStr());
}); });
} }
@@ -1294,36 +1321,36 @@ void structToString(const v8::FunctionCallbackInfo<v8::Value>& args) {
struct FromJsConverter { struct FromJsConverter {
CapnpContext& context; CapnpContext& context;
v8::Handle<v8::Value> contextHandle; v8::Local<v8::Value> contextHandle;
v8::Handle<v8::Function> localCapType; v8::Local<v8::Function> localCapType;
capnp::DynamicCapability::Client fromLocalCap( capnp::DynamicCapability::Client fromLocalCap(
capnp::InterfaceSchema schema, v8::Handle<v8::Object> object); capnp::InterfaceSchema schema, v8::Local<v8::Object> object);
capnp::Orphan<capnp::DynamicValue> int64FromJs(v8::Handle<v8::Value> js) { capnp::Orphan<capnp::DynamicValue> int64FromJs(v8::Local<v8::Value> js) {
if (js->IsNumber()) { if (js->IsNumber()) {
return js->IntegerValue(); return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
} else { } else {
KJV8_STACK_STR(text, js, 32); KJV8_STACK_STR(text, js, 32);
char* end; char* end;
int64_t result = strtoll(text.cStr(), &end, 0); int64_t result = strtoll(text.cStr(), &end, 0);
if (text.size() == 0 || *end != '\0') { if (text.size() == 0 || *end != '\0') {
return js->IntegerValue(); return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
} else { } else {
return result; return result;
} }
} }
} }
capnp::Orphan<capnp::DynamicValue> uint64FromJs(v8::Handle<v8::Value> js) { capnp::Orphan<capnp::DynamicValue> uint64FromJs(v8::Local<v8::Value> js) {
if (js->IsNumber()) { if (js->IsNumber()) {
return js->IntegerValue(); return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
} else { } else {
KJV8_STACK_STR(text, js, 32); KJV8_STACK_STR(text, js, 32);
char* end; char* end;
uint64_t result = strtoull(text.cStr(), &end, 0); uint64_t result = strtoull(text.cStr(), &end, 0);
if (text.size() == 0 || *end != '\0') { if (text.size() == 0 || *end != '\0') {
return js->IntegerValue(); return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
} else { } else {
return result; return result;
} }
@@ -1332,30 +1359,30 @@ struct FromJsConverter {
capnp::Orphan<capnp::DynamicValue> orphanFromJs( capnp::Orphan<capnp::DynamicValue> orphanFromJs(
capnp::StructSchema::Field field, capnp::Orphanage orphanage, capnp::StructSchema::Field field, capnp::Orphanage orphanage,
capnp::Type type, v8::Handle<v8::Value> js) { capnp::Type type, v8::Local<v8::Value> js) {
switch (type.which()) { switch (type.which()) {
case capnp::schema::Type::VOID: case capnp::schema::Type::VOID:
// Accept any false-y value. // Accept any false-y value.
if (!js->BooleanValue()) { if (!js->BooleanValue(v8::Isolate::GetCurrent())) {
return capnp::VOID; return capnp::VOID;
} }
break; break;
case capnp::schema::Type::BOOL: return js->BooleanValue(); case capnp::schema::Type::BOOL: return js->BooleanValue(v8::Isolate::GetCurrent());
case capnp::schema::Type::INT8: return js->IntegerValue(); case capnp::schema::Type::INT8: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::INT16: return js->IntegerValue(); case capnp::schema::Type::INT16: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::INT32: return js->IntegerValue(); case capnp::schema::Type::INT32: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::INT64: return int64FromJs(js); case capnp::schema::Type::INT64: return int64FromJs(js);
case capnp::schema::Type::UINT8: return js->IntegerValue(); case capnp::schema::Type::UINT8: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::UINT16: return js->IntegerValue(); case capnp::schema::Type::UINT16: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::UINT32: return js->IntegerValue(); case capnp::schema::Type::UINT32: return js->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::UINT64: return uint64FromJs(js); case capnp::schema::Type::UINT64: return uint64FromJs(js);
case capnp::schema::Type::FLOAT32: return js->NumberValue(); case capnp::schema::Type::FLOAT32: return js->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::FLOAT64: return js->NumberValue(); case capnp::schema::Type::FLOAT64: return js->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
case capnp::schema::Type::TEXT: { case capnp::schema::Type::TEXT: {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
auto str = js->ToString(); auto str = js->ToString(v8::Isolate::GetCurrent());
capnp::Orphan<capnp::Text> orphan = orphanage.newOrphan<capnp::Text>(str->Utf8Length()); capnp::Orphan<capnp::Text> orphan = orphanage.newOrphan<capnp::Text>(str->Utf8Length(v8::Isolate::GetCurrent()));
str->WriteUtf8(orphan.get().begin()); str->WriteUtf8(v8::Isolate::GetCurrent(), orphan.get().begin(), orphan.get().size());
return kj::mv(orphan); return kj::mv(orphan);
} }
case capnp::schema::Type::DATA: case capnp::schema::Type::DATA:
@@ -1413,7 +1440,7 @@ struct FromJsConverter {
KJ_IF_MAYBE(enumerant, schema.findEnumerantByName(name)) { KJ_IF_MAYBE(enumerant, schema.findEnumerantByName(name)) {
return capnp::DynamicEnum(*enumerant); return capnp::DynamicEnum(*enumerant);
} else if (js->IsUint32()) { } else if (js->IsUint32()) {
return capnp::DynamicEnum(schema, js->Uint32Value()); return capnp::DynamicEnum(schema, js->Uint32Value(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked());
} }
break; break;
} }
@@ -1439,8 +1466,10 @@ struct FromJsConverter {
} else KJ_IF_MAYBE(cap, Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(js)) { } else KJ_IF_MAYBE(cap, Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(js)) {
return orphanage.newOrphanCopy(*cap); return orphanage.newOrphanCopy(*cap);
} else if (!localCapType.IsEmpty()) { } else if (!localCapType.IsEmpty()) {
v8::Handle<v8::Value> arg = js; v8::Local<v8::Value> arg = js;
auto wrapped = localCapType->NewInstance(1, &arg); auto wrapped = localCapType->NewInstance(
v8::Isolate::GetCurrent()->GetCurrentContext(), 1, &arg)
.ToLocalChecked();
if (!wrapped.IsEmpty()) { if (!wrapped.IsEmpty()) {
auto cap = fromLocalCap(schema, wrapped); auto cap = fromLocalCap(schema, wrapped);
return orphanage.newOrphanCopy(cap); return orphanage.newOrphanCopy(cap);
@@ -1457,8 +1486,10 @@ struct FromJsConverter {
} else KJ_IF_MAYBE(cap, Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(js)) { } else KJ_IF_MAYBE(cap, Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(js)) {
return orphanage.newOrphanCopy(*cap); return orphanage.newOrphanCopy(*cap);
} else if (!localCapType.IsEmpty()) { } else if (!localCapType.IsEmpty()) {
v8::Handle<v8::Value> arg = js; v8::Local<v8::Value> arg = js;
auto wrapped = localCapType->NewInstance(1, &arg); auto wrapped = localCapType->NewInstance(
v8::Isolate::GetCurrent()->GetCurrentContext(), 1, &arg)
.ToLocalChecked();
if (!wrapped.IsEmpty()) { if (!wrapped.IsEmpty()) {
auto cap = fromLocalCap(capnp::Schema::from<capnp::Capability>(), wrapped); auto cap = fromLocalCap(capnp::Schema::from<capnp::Capability>(), wrapped);
return orphanage.newOrphanCopy(cap); return orphanage.newOrphanCopy(cap);
@@ -1493,7 +1524,7 @@ struct FromJsConverter {
} }
bool fieldFromJs(capnp::DynamicStruct::Builder builder, capnp::StructSchema::Field field, bool fieldFromJs(capnp::DynamicStruct::Builder builder, capnp::StructSchema::Field field,
v8::Handle<v8::Value> js) { v8::Local<v8::Value> js) {
if ((js->IsNull() || js->IsUndefined()) && !field.getType().isVoid()) { if ((js->IsNull() || js->IsUndefined()) && !field.getType().isVoid()) {
return true; return true;
} }
@@ -1528,12 +1559,12 @@ struct FromJsConverter {
auto schema = builder.getSchema(); auto schema = builder.getSchema();
// for (auto field: schema.getFields()) { // for (auto field: schema.getFields()) {
// kj::StringPtr name = field.getProto().getName(); // kj::StringPtr name = field.getProto().getName();
// v8::Handle<v8::Value> value = js->Get(newSymbol(name.begin(), name.size())); // v8::Local<v8::Value> value = js->Get(newSymbol(name.begin(), name.size()));
// if (!value.IsEmpty() && !value->IsUndefined()) { // if (!value.IsEmpty() && !value->IsUndefined()) {
// fieldFromJs(builder, field, value); // fieldFromJs(builder, field, value);
// } // }
// } // }
v8::Local<v8::Array> fieldNames = js->GetPropertyNames(); v8::Local<v8::Array> fieldNames = js->GetPropertyNames(v8::Isolate::GetCurrent()->GetCurrentContext()).ToLocalChecked();
for (uint i: kj::range(0u, fieldNames->Length())) { for (uint i: kj::range(0u, fieldNames->Length())) {
auto jsName = fieldNames->Get(i); auto jsName = fieldNames->Get(i);
KJV8_STACK_STR(fieldName, jsName, 32); KJV8_STACK_STR(fieldName, jsName, 32);
@@ -1562,11 +1593,11 @@ void fromJs(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP_BUILDER(builder, args[0]); KJV8_UNWRAP_BUILDER(builder, args[0]);
v8::Handle<v8::Value> jsValue = args[1]; v8::Local<v8::Value> jsValue = args[1];
v8::Handle<v8::Function> localCapType = emptyHandle; v8::Local<v8::Function> localCapType = emptyHandle;
if (args[2]->IsFunction()) { if (args[2]->IsFunction()) {
localCapType = v8::Handle<v8::Function>::Cast(args[2]); localCapType = v8::Local<v8::Function>::Cast(args[2]);
} }
liftKj(args, [&]() -> void { liftKj(args, [&]() -> void {
@@ -1595,12 +1626,12 @@ void fromJs(const v8::FunctionCallbackInfo<v8::Value>& args) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool fieldToJs(CapnpContext& context, v8::Handle<v8::Object> object, bool fieldToJs(CapnpContext& context, v8::Local<v8::Object> object,
capnp::DynamicStruct::Reader reader, capnp::StructSchema::Field field, capnp::DynamicStruct::Reader reader, capnp::StructSchema::Field field,
v8::Handle<v8::Value> capConstructor); v8::Local<v8::Value> capConstructor);
v8::Handle<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Reader value, v8::Local<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Reader value,
capnp::Type type, v8::Handle<v8::Value> capConstructor) { capnp::Type type, v8::Local<v8::Value> capConstructor) {
switch (value.getType()) { switch (value.getType()) {
case capnp::DynamicValue::UNKNOWN: case capnp::DynamicValue::UNKNOWN:
return v8::Undefined(v8::Isolate::GetCurrent()); return v8::Undefined(v8::Isolate::GetCurrent());
@@ -1683,11 +1714,13 @@ v8::Handle<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Read
v8::EscapableHandleScope scope(v8::Isolate::GetCurrent()); v8::EscapableHandleScope scope(v8::Isolate::GetCurrent());
auto cap = value.as<capnp::DynamicCapability>(); auto cap = value.as<capnp::DynamicCapability>();
capnp::Schema schema = cap.getSchema(); capnp::Schema schema = cap.getSchema();
v8::Handle<v8::Value> result = context.wrapper.wrapCopy(kj::mv(cap)); v8::Local<v8::Value> result = context.wrapper.wrapCopy(kj::mv(cap));
if (capConstructor->IsFunction()) { if (capConstructor->IsFunction()) {
v8::Function* func = v8::Function::Cast(*capConstructor); v8::Function* func = v8::Function::Cast(*capConstructor);
v8::Handle<v8::Value> args[2] = { result, context.wrapper.wrapCopy(schema) }; v8::Local<v8::Value> args[2] = { result, context.wrapper.wrapCopy(schema) };
result = func->NewInstance(kj::size(args), args); result = func->NewInstance(
v8::Isolate::GetCurrent()->GetCurrentContext(), kj::size(args), args)
.ToLocalChecked();
if (result.IsEmpty()) { if (result.IsEmpty()) {
return emptyHandle; return emptyHandle;
} }
@@ -1699,11 +1732,13 @@ v8::Handle<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Read
v8::EscapableHandleScope scope(v8::Isolate::GetCurrent()); v8::EscapableHandleScope scope(v8::Isolate::GetCurrent());
auto cap = value.as<capnp::AnyPointer>().getAs<capnp::Capability>(); auto cap = value.as<capnp::AnyPointer>().getAs<capnp::Capability>();
capnp::DynamicCapability::Client dynamicCap(kj::mv(cap)); capnp::DynamicCapability::Client dynamicCap(kj::mv(cap));
v8::Handle<v8::Value> result = context.wrapper.wrapCopy(kj::mv(dynamicCap)); v8::Local<v8::Value> result = context.wrapper.wrapCopy(kj::mv(dynamicCap));
if (capConstructor->IsFunction()) { if (capConstructor->IsFunction()) {
v8::Function* func = v8::Function::Cast(*capConstructor); v8::Function* func = v8::Function::Cast(*capConstructor);
v8::Handle<v8::Value> args[1] = { result }; v8::Local<v8::Value> args[1] = { result };
result = func->NewInstance(kj::size(args), args); result = func->NewInstance(
v8::Isolate::GetCurrent()->GetCurrentContext(), kj::size(args), args)
.ToLocalChecked();
if (result.IsEmpty()) { if (result.IsEmpty()) {
return emptyHandle; return emptyHandle;
} }
@@ -1720,11 +1755,11 @@ v8::Handle<v8::Value> valueToJs(CapnpContext& context, capnp::DynamicValue::Read
KJ_FAIL_ASSERT("Unimplemented DynamicValue type."); KJ_FAIL_ASSERT("Unimplemented DynamicValue type.");
} }
bool fieldToJs(CapnpContext& context, v8::Handle<v8::Object> object, bool fieldToJs(CapnpContext& context, v8::Local<v8::Object> object,
capnp::DynamicStruct::Reader reader, capnp::StructSchema::Field field, capnp::DynamicStruct::Reader reader, capnp::StructSchema::Field field,
v8::Handle<v8::Value> capConstructor) { v8::Local<v8::Value> capConstructor) {
auto proto = field.getProto(); auto proto = field.getProto();
v8::Handle<v8::Value> fieldValue; v8::Local<v8::Value> fieldValue;
switch (proto.which()) { switch (proto.which()) {
case capnp::schema::Field::SLOT: case capnp::schema::Field::SLOT:
fieldValue = valueToJs(context, reader.get(field), field.getType(), capConstructor); fieldValue = valueToJs(context, reader.get(field), field.getType(), capConstructor);
@@ -1755,7 +1790,7 @@ void toJs(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP_READER(reader, args[0]); KJV8_UNWRAP_READER(reader, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return valueToJs(context, reader, reader.getSchema(), args[1]); return valueToJs(context, reader, reader.getSchema(), args[1]);
}); });
} }
@@ -1769,7 +1804,7 @@ void toJsParams(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP_READER(reader, args[0]); KJV8_UNWRAP_READER(reader, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
auto schema = reader.getSchema(); auto schema = reader.getSchema();
if (schema.getProto().getScopeId() == 0) { if (schema.getProto().getScopeId() == 0) {
// This appears to be a parameter set. // This appears to be a parameter set.
@@ -1797,10 +1832,10 @@ void expectedSizeFromPrefix(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
v8::Handle<v8::Value> bufferHandle = args[0]; v8::Local<v8::Value> bufferHandle = args[0];
KJV8_UNWRAP_BUFFER(buffer, bufferHandle); KJV8_UNWRAP_BUFFER(buffer, bufferHandle);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
kj::ArrayPtr<const capnp::word> words; kj::ArrayPtr<const capnp::word> words;
kj::Array<capnp::word> copy; kj::Array<capnp::word> copy;
if (reinterpret_cast<uintptr_t>(buffer.begin()) % sizeof(capnp::word) != 0) { if (reinterpret_cast<uintptr_t>(buffer.begin()) % sizeof(capnp::word) != 0) {
@@ -1823,7 +1858,7 @@ void fromBytes(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
v8::Handle<v8::Value> bufferHandle = args[0]; v8::Local<v8::Value> bufferHandle = args[0];
KJV8_UNWRAP_BUFFER(buffer, bufferHandle); KJV8_UNWRAP_BUFFER(buffer, bufferHandle);
KJV8_UNWRAP(capnp::Schema, schema, args[1]); KJV8_UNWRAP(capnp::Schema, schema, args[1]);
@@ -1835,10 +1870,10 @@ void fromBytes(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_TYPE_ERROR(obj, v8::Object); KJV8_TYPE_ERROR(obj, v8::Object);
} }
return liftKj(args, [&]() -> v8::Handle<v8::Value> { return liftKj(args, [&]() -> v8::Local<v8::Value> {
auto options = args[2].As<v8::Object>(); auto options = args[2].As<v8::Object>();
bool packed = options->Get(newSymbol("packed"))->BooleanValue(); bool packed = options->Get(newSymbol("packed"))->BooleanValue(v8::Isolate::GetCurrent());
bool flat = options->Get(newSymbol("flat"))->BooleanValue(); bool flat = options->Get(newSymbol("flat"))->BooleanValue(v8::Isolate::GetCurrent());
v8::Local<v8::Object> wrapper; v8::Local<v8::Object> wrapper;
@@ -1876,7 +1911,11 @@ void fromBytes(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
} }
wrapper->SetHiddenValue(newSymbol("buffer"), bufferHandle); v8::Isolate* isolate = v8::Isolate::GetCurrent();
wrapper->SetPrivate(
isolate->GetCurrentContext(),
v8::Private::ForApi(isolate, newSymbol("capnp::buffer")),
bufferHandle);
return wrapper; return wrapper;
}); });
} }
@@ -1889,10 +1928,10 @@ void toBytes(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_TYPE_ERROR(obj, v8::Object); KJV8_TYPE_ERROR(obj, v8::Object);
} }
return liftKj(args, [&]() -> v8::Handle<v8::Value> { return liftKj(args, [&]() -> v8::Local<v8::Value> {
auto options = args[1].As<v8::Object>(); auto options = args[1].As<v8::Object>();
bool packed = options->Get(newSymbol("packed"))->BooleanValue(); bool packed = options->Get(newSymbol("packed"))->BooleanValue(v8::Isolate::GetCurrent());
bool flat = options->Get(newSymbol("flat"))->BooleanValue(); bool flat = options->Get(newSymbol("flat"))->BooleanValue(v8::Isolate::GetCurrent());
if (flat) { if (flat) {
// Write whole message to flat array. // Write whole message to flat array.
@@ -1978,7 +2017,7 @@ void connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_STACK_STR(address, args[0], 64); KJV8_STACK_STR(address, args[0], 64);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
kj::Maybe<capnp::Capability::Client> bootstrap = kj::Maybe<capnp::Capability::Client> bootstrap =
Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(args[1]); Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(args[1]);
@@ -2003,9 +2042,9 @@ void connectUnixFd(const v8::FunctionCallbackInfo<v8::Value>& args) {
// the existing socket. // the existing socket.
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
int fd = args[0]->IntegerValue(); int fd = args[0]->IntegerValue(v8::Isolate::GetCurrent()->GetCurrentContext()).ToChecked();
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
int fdPair[2]; int fdPair[2];
KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fdPair)); KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fdPair));
kj::AutoCloseFd clientEnd(fdPair[0]); kj::AutoCloseFd clientEnd(fdPair[0]);
@@ -2080,7 +2119,7 @@ void restore(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto ref = toKjString(args[1]); // TODO(soon): Allow struct reader. auto ref = toKjString(args[1]); // TODO(soon): Allow struct reader.
KJV8_UNWRAP(capnp::Schema, schema, args[2]); KJV8_UNWRAP(capnp::Schema, schema, args[2]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
KJ_REQUIRE(schema.getProto().isInterface(), KJ_REQUIRE(schema.getProto().isInterface(),
"not an interface type", schema.getProto().getDisplayName()); "not an interface type", schema.getProto().getDisplayName());
@@ -2102,7 +2141,7 @@ void castAs(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]); KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]);
KJV8_UNWRAP(capnp::Schema, schema, args[1]); KJV8_UNWRAP(capnp::Schema, schema, args[1]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
KJ_REQUIRE(schema.getProto().isInterface(), KJ_REQUIRE(schema.getProto().isInterface(),
"not an interface type", schema.getProto().getDisplayName()); "not an interface type", schema.getProto().getDisplayName());
return context.wrapper.wrapCopy(cap.castAs<capnp::DynamicCapability>(schema.asInterface())); return context.wrapper.wrapCopy(cap.castAs<capnp::DynamicCapability>(schema.asInterface()));
@@ -2118,7 +2157,7 @@ void schemaFor(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]); KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return context.wrapper.wrapCopy(capnp::Schema(cap.getSchema())); return context.wrapper.wrapCopy(capnp::Schema(cap.getSchema()));
}); });
} }
@@ -2161,7 +2200,7 @@ void dupCap(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]); KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return context.wrapper.wrapCopy(cap); return context.wrapper.wrapCopy(cap);
}); });
} }
@@ -2191,48 +2230,57 @@ void request(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]); KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[0]);
KJV8_UNWRAP(capnp::InterfaceSchema::Method, method, args[1]); KJV8_UNWRAP(capnp::InterfaceSchema::Method, method, args[1]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return context.wrapper.wrapCopy(ClientRequest { cap.newRequest(method) }); return context.wrapper.wrapCopy(ClientRequest { cap.newRequest(method) });
}); });
} }
void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeline, void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeline,
v8::Handle<v8::Object> js, v8::Handle<v8::Value> capConstructor); v8::Local<v8::Object> js, v8::Local<v8::Value> capConstructor);
v8::Handle<v8::Object> pipelineStructFieldToJs(CapnpContext& context, v8::Local<v8::Object> pipelineStructFieldToJs(CapnpContext& context,
capnp::DynamicStruct::Pipeline& pipeline, capnp::DynamicStruct::Pipeline& pipeline,
capnp::StructSchema::Field field, capnp::StructSchema::Field field,
v8::Handle<v8::Value> capConstructor) { v8::Local<v8::Value> capConstructor) {
v8::Handle<v8::Object> fieldValue = v8::Object::New(v8::Isolate::GetCurrent()); v8::Local<v8::Object> fieldValue = v8::Object::New(v8::Isolate::GetCurrent());
pipelineToJs(context, pipeline.get(field).releaseAs<capnp::DynamicStruct>(), pipelineToJs(context, pipeline.get(field).releaseAs<capnp::DynamicStruct>(),
fieldValue, capConstructor); fieldValue, capConstructor);
return fieldValue; return fieldValue;
} }
void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeline, void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeline,
v8::Handle<v8::Object> js, v8::Handle<v8::Value> capConstructor) { v8::Local<v8::Object> js, v8::Local<v8::Value> capConstructor) {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
capnp::StructSchema schema = pipeline.getSchema(); capnp::StructSchema schema = pipeline.getSchema();
for (capnp::StructSchema::Field field: schema.getNonUnionFields()) { for (capnp::StructSchema::Field field: schema.getNonUnionFields()) {
auto proto = field.getProto(); auto proto = field.getProto();
v8::Handle<v8::Object> fieldValue; v8::Local<v8::Object> fieldValue;
switch (proto.which()) { switch (proto.which()) {
case capnp::schema::Field::SLOT: case capnp::schema::Field::SLOT: {
switch (proto.getSlot().getType().which()) { auto type = field.getType();
switch (type.which()) {
case capnp::schema::Type::STRUCT: case capnp::schema::Type::STRUCT:
fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor); fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor);
break; break;
case capnp::schema::Type::ANY_POINTER:
if (type.whichAnyPointerKind() !=
capnp::schema::Type::AnyPointer::Unconstrained::CAPABILITY) {
continue;
}
// fallthrough
case capnp::schema::Type::INTERFACE: { case capnp::schema::Type::INTERFACE: {
auto cap = pipeline.get(field).releaseAs<capnp::DynamicCapability>(); auto cap = pipeline.get(field).releaseAs<capnp::DynamicCapability>();
capnp::Schema capSchema = cap.getSchema(); capnp::Schema capSchema = cap.getSchema();
fieldValue = context.wrapper.wrapCopy(kj::mv(cap)); fieldValue = context.wrapper.wrapCopy(kj::mv(cap));
if (!capConstructor->IsUndefined() && capConstructor->IsFunction()) { if (!capConstructor->IsUndefined() && capConstructor->IsFunction()) {
v8::Function* func = v8::Function::Cast(*capConstructor); v8::Function* func = v8::Function::Cast(*capConstructor);
v8::Handle<v8::Value> args[2] = { fieldValue, v8::Local<v8::Value> args[2] = { fieldValue,
context.wrapper.wrapCopy(kj::mv(capSchema)) }; context.wrapper.wrapCopy(kj::mv(capSchema)) };
fieldValue = func->NewInstance(kj::size(args), args); fieldValue = func->NewInstance(
v8::Isolate::GetCurrent()->GetCurrentContext(), kj::size(args), args)
.ToLocalChecked();
} }
break; break;
} }
@@ -2240,6 +2288,7 @@ void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeli
continue; continue;
} }
break; break;
}
case capnp::schema::Field::GROUP: case capnp::schema::Field::GROUP:
fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor); fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor);
@@ -2279,9 +2328,9 @@ void send(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
OwnHandle<v8::Function> callback = v8::Handle<v8::Function>::Cast(args[1]); OwnHandle<v8::Function> callback = v8::Local<v8::Function>::Cast(args[1]);
OwnHandle<v8::Function> errorCallback = v8::Handle<v8::Function>::Cast(args[2]); OwnHandle<v8::Function> errorCallback = v8::Local<v8::Function>::Cast(args[2]);
auto promise = KJ_REQUIRE_NONNULL(request.request, "Request already sent.").send(); auto promise = KJ_REQUIRE_NONNULL(request.request, "Request already sent.").send();
request.request = nullptr; request.request = nullptr;
@@ -2291,7 +2340,7 @@ void send(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto canceler = kj::refcounted<Canceler>(); auto canceler = kj::refcounted<Canceler>();
canceler->fulfiller = kj::mv(cancelerPaf.fulfiller); canceler->fulfiller = kj::mv(cancelerPaf.fulfiller);
v8::Handle<v8::Object> result = context.wrapper.wrapCopy(kj::addRef(*canceler)); v8::Local<v8::Object> result = context.wrapper.wrapCopy(kj::addRef(*canceler));
// Wait for results and call the callback. Note that we can safely capture `context` by // Wait for results and call the callback. Note that we can safely capture `context` by
// reference because if the context is destroyed, the event loop will stop running. // reference because if the context is destroyed, the event loop will stop running.
@@ -2301,7 +2350,7 @@ void send(const v8::FunctionCallbackInfo<v8::Value>& args) {
[&context](OwnHandle<v8::Function>&& callback, [&context](OwnHandle<v8::Function>&& callback,
capnp::Response<capnp::DynamicStruct>&& response) { capnp::Response<capnp::DynamicStruct>&& response) {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Handle<v8::Value> args[1] = { v8::Local<v8::Value> args[1] = {
context.wrapper.wrapCopy(ClientResponse { kj::mv(response) }) context.wrapper.wrapCopy(ClientResponse { kj::mv(response) })
}; };
// TODO(cleanup): Call() demands an Object parameter but `undefined` is not an object. So // TODO(cleanup): Call() demands an Object parameter but `undefined` is not an object. So
@@ -2312,7 +2361,7 @@ void send(const v8::FunctionCallbackInfo<v8::Value>& args) {
[&context](OwnHandle<v8::Function>&& errorCallback, [&context](OwnHandle<v8::Function>&& errorCallback,
kj::Exception&& exception) { kj::Exception&& exception) {
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Handle<v8::Value> args[1] = { toJsException(kj::mv(exception)) }; v8::Local<v8::Value> args[1] = { toJsException(kj::mv(exception)) };
node::MakeCallback(scope.GetIsolate(), v8::Object::New(scope.GetIsolate()), node::MakeCallback(scope.GetIsolate(), v8::Object::New(scope.GetIsolate()),
errorCallback.get(), 1, args); errorCallback.get(), 1, args);
})); }));
@@ -2339,56 +2388,22 @@ void cancel(const v8::FunctionCallbackInfo<v8::Value>& args) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Local caps // Local caps
struct TypeHash {
inline size_t operator()(const capnp::Type& t) const {
return t.hashCode();
}
};
std::unordered_map<capnp::Type, uint, TypeHash> localCapTypeCounts;
void dumpLocalCapTypeCounts(const v8::FunctionCallbackInfo<v8::Value>& args) {
liftKj(args, [&]() -> void {
typedef std::pair<capnp::Type, uint> E;
kj::Array<E> array = KJ_MAP(e, localCapTypeCounts) { return E(e); };
std::sort(array.begin(), array.end(),
[](const E& a, const E& b) { return a.second > b.second; });
auto lines = KJ_MAP(e, array) {
return kj::str('\n', e.second, " ", e.first.asInterface().getProto().getDisplayName());
};
auto typeStats = kj::strArray(lines, "");
KJ_LOG(WARNING, "dumping types of local capabilities", typeStats);
});
}
class LocalCap final: public capnp::DynamicCapability::Server { class LocalCap final: public capnp::DynamicCapability::Server {
public: public:
LocalCap(capnp::InterfaceSchema schema, v8::Handle<v8::Object> object, LocalCap(capnp::InterfaceSchema schema, v8::Local<v8::Object> object,
CapnpContext& capnpContext, v8::Handle<v8::Value> capnpContextHandle) CapnpContext& capnpContext, v8::Local<v8::Value> capnpContextHandle)
: capnp::DynamicCapability::Server(schema), : capnp::DynamicCapability::Server(schema),
object(object), capnpContext(capnpContext), capnpContextHandle(capnpContextHandle) { object(object), capnpContext(capnpContext), capnpContextHandle(capnpContextHandle) {}
localCapTypeCounts[schema]++;
}
~LocalCap() { ~LocalCap() {
// Call the object's close() method if it has one, so that it can react to the handle being // Call the object's close() method if it has one, so that it can react to the handle being
// dropped. // dropped.
auto iter = localCapTypeCounts.find(getSchema());
if (iter == localCapTypeCounts.end()) {
KJ_LOG(ERROR, "inconsistent type counts");
} else if (--iter->second == 0) {
localCapTypeCounts.erase(iter);
}
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
auto jsMethod = object->Get(newSymbol("close")); auto jsMethod = object->Get(newSymbol("close"));
if (jsMethod->IsFunction()) { if (jsMethod->IsFunction()) {
auto func = v8::Handle<v8::Function>::Cast(jsMethod); auto func = v8::Local<v8::Function>::Cast(jsMethod);
node::MakeCallback(scope.GetIsolate(), object.get(), func, 0, nullptr); node::MakeCallback(scope.GetIsolate(), object.get(), func, 0, nullptr);
} }
} }
@@ -2404,7 +2419,7 @@ public:
KJ_FAIL_ASSERT("Method not implemented.", name) { break; } KJ_FAIL_ASSERT("Method not implemented.", name) { break; }
return kj::READY_NOW; return kj::READY_NOW;
} }
auto func = v8::Handle<v8::Function>::Cast(jsMethod); auto func = v8::Local<v8::Function>::Cast(jsMethod);
auto paf = kj::newPromiseAndFulfiller<void>(); auto paf = kj::newPromiseAndFulfiller<void>();
@@ -2413,7 +2428,7 @@ public:
request.context = context; request.context = context;
request.params = context.getParams(); request.params = context.getParams();
v8::Handle<v8::Value> arg = capnpContext.wrapper.wrapCopy(kj::mv(request)); v8::Local<v8::Value> arg = capnpContext.wrapper.wrapCopy(kj::mv(request));
node::MakeCallback(scope.GetIsolate(), object.get(), func, 1, &arg); node::MakeCallback(scope.GetIsolate(), object.get(), func, 1, &arg);
return kj::mv(paf.promise); return kj::mv(paf.promise);
} }
@@ -2425,7 +2440,7 @@ private:
}; };
capnp::DynamicCapability::Client FromJsConverter::fromLocalCap( capnp::DynamicCapability::Client FromJsConverter::fromLocalCap(
capnp::InterfaceSchema schema, v8::Handle<v8::Object> object) { capnp::InterfaceSchema schema, v8::Local<v8::Object> object) {
return kj::heap<LocalCap>(schema, object, context, contextHandle); return kj::heap<LocalCap>(schema, object, context, contextHandle);
} }
@@ -2447,10 +2462,10 @@ void newCap(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_TYPE_ERROR(obj, v8::Object); KJV8_TYPE_ERROR(obj, v8::Object);
} }
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return context.wrapper.wrapCopy(capnp::DynamicCapability::Client( return context.wrapper.wrapCopy(capnp::DynamicCapability::Client(
kj::heap<LocalCap>(schema.asInterface(), kj::heap<LocalCap>(schema.asInterface(),
v8::Handle<v8::Object>::Cast(args[1]), v8::Local<v8::Object>::Cast(args[1]),
context, args.Data()))); context, args.Data())));
}); });
} }
@@ -2467,7 +2482,7 @@ void newPromisedCap(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_TYPE_ERROR(schema, capnp::InterfaceSchema); KJV8_TYPE_ERROR(schema, capnp::InterfaceSchema);
} }
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
auto paf = kj::newPromiseAndFulfiller<capnp::Capability::Client>(); auto paf = kj::newPromiseAndFulfiller<capnp::Capability::Client>();
v8::Local<v8::Object> result = v8::Object::New(v8::Isolate::GetCurrent()); v8::Local<v8::Object> result = v8::Object::New(v8::Isolate::GetCurrent());
@@ -2521,7 +2536,7 @@ void isCap(const v8::FunctionCallbackInfo<v8::Value>& args) {
// //
// If `value` is a capability, return true. // If `value` is a capability, return true.
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
return v8::Boolean::New(v8::Isolate::GetCurrent(), return v8::Boolean::New(v8::Isolate::GetCurrent(),
Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(args[0]) != nullptr); Wrapper::tryUnwrap<capnp::DynamicCapability::Client>(args[0]) != nullptr);
}); });
@@ -2551,7 +2566,7 @@ void getResults(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP(CapnpContext, context, args.Data()); KJV8_UNWRAP(CapnpContext, context, args.Data());
KJV8_UNWRAP(ServerRequest, request, args[0]); KJV8_UNWRAP(ServerRequest, request, args[0]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
kj::Own<ServerResults> results; kj::Own<ServerResults> results;
KJ_IF_MAYBE(existing, request.results) { KJ_IF_MAYBE(existing, request.results) {
results = kj::addRef(**existing); results = kj::addRef(**existing);
@@ -2756,7 +2771,7 @@ void matchPowerboxQuery(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP_BUFFER(queryBuffer, args[0]); KJV8_UNWRAP_BUFFER(queryBuffer, args[0]);
KJV8_UNWRAP_BUFFER(candidateBuffer, args[1]); KJV8_UNWRAP_BUFFER(candidateBuffer, args[1]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
AlignedWords queryWords(queryBuffer); AlignedWords queryWords(queryBuffer);
AlignedWords candidateWords(candidateBuffer); AlignedWords candidateWords(candidateBuffer);
@@ -2777,7 +2792,7 @@ void chacha20(const v8::FunctionCallbackInfo<v8::Value>& args) {
KJV8_UNWRAP_BUFFER(nonce, args[1]); KJV8_UNWRAP_BUFFER(nonce, args[1]);
KJV8_UNWRAP_BUFFER(key, args[2]); KJV8_UNWRAP_BUFFER(key, args[2]);
liftKj(args, [&]() -> v8::Handle<v8::Value> { liftKj(args, [&]() -> v8::Local<v8::Value> {
KJ_REQUIRE(key.size() == crypto_stream_chacha20_KEYBYTES); KJ_REQUIRE(key.size() == crypto_stream_chacha20_KEYBYTES);
KJ_REQUIRE(nonce.size() == crypto_stream_chacha20_NONCEBYTES); KJ_REQUIRE(nonce.size() == crypto_stream_chacha20_NONCEBYTES);
@@ -2796,18 +2811,43 @@ void chacha20(const v8::FunctionCallbackInfo<v8::Value>& args) {
}); });
} }
namespace {
void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32];
// ignoreCount = 2 to ignore crashHandler() and signal trampoline.
auto trace = kj::getStackTrace(traceSpace, 2);
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", stringifyStackTraceAddresses(trace), '\n');
kj::FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
_exit(1);
}
} // namespace
void catchSegfaults() {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND;
action.sa_sigaction = &crashHandler;
KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr));
}
#endif // SANDSTORM_BUILD #endif // SANDSTORM_BUILD
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void init(v8::Handle<v8::Object> exports) { void init(v8::Local<v8::Object> exports) {
CapnpContext* context = new CapnpContext; CapnpContext* context = new CapnpContext;
auto wrappedContext = context->wrapper.wrap(context); auto wrappedContext = context->wrapper.wrap(context);
auto mapFunction = [&](const char* name, v8::FunctionCallback callback) { auto mapFunction = [&](const char* name, v8::FunctionCallback callback) {
exports->Set(newSymbol(name), exports->Set(newSymbol(name),
v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), callback, wrappedContext) v8::FunctionTemplate::New(v8::Isolate::GetCurrent(), callback, wrappedContext)
->GetFunction()); ->GetFunction(v8::Isolate::GetCurrent()->GetCurrentContext()).ToLocalChecked());
}; };
mapFunction("setNative", setNative); mapFunction("setNative", setNative);
@@ -2848,9 +2888,8 @@ void init(v8::Handle<v8::Object> exports) {
#ifdef SANDSTORM_BUILD #ifdef SANDSTORM_BUILD
mapFunction("matchPowerboxQuery", matchPowerboxQuery); mapFunction("matchPowerboxQuery", matchPowerboxQuery);
mapFunction("chacha20", chacha20); mapFunction("chacha20", chacha20);
catchSegfaults();
#endif #endif
mapFunction("dumpLocalCapTypeCounts", dumpLocalCapTypeCounts);
} }
} // namespace } // namespace
+45
View File
@@ -0,0 +1,45 @@
declare module Capnp {
type Id = string;
abstract class StructSchema<Builder, Reader> {
// A schema for a struct type. The `Builder` parameter is what users of
// the library specify in order to create a value; it may be missing fields,
// which will be filled in with defaults. A `Reader` is what is is returned
// when parsing a value, and is guaranteed to have all known fields set.
typeId: Id;
}
abstract class InterfaceSchema<Server, Client> {
// A schema for an interface type. Similarly to `StructSchema`, the `Server`
// and `Client` parameters represent objects which may have missing
// methods (for servers), and ones which do not (for clients, though calling
// the method may still throw unimplemented).
typeId: Id;
}
interface AnyServer {
close?: () => void;
}
interface AnyClient {
close(): void;
closed: boolean;
castAs<Server, Client>(schema: InterfaceSchema<Server, Client>): Client;
}
const Capability: {
new<Server, Client>(server: Server, schema: InterfaceSchema<Server, Client>): Client;
}
abstract class Connection {
restore<Server, Client>(exportName: string, type: InterfaceSchema<Server, Client>): Client;
close(): void;
}
function parse<Builder, Reader>(type: StructSchema<Builder, Reader>, buffer: Buffer): Reader;
function serialize<Builder, Reader>(type: StructSchema<Builder, Reader>, builder: Builder): Buffer;
function connect(addr: string): Connection;
}
export default Capnp;
+13 -11
View File
@@ -192,16 +192,20 @@ function wrapLocalMethod(self, method) {
} }
function LocalCapWrapper(obj) { function LocalCapWrapper(obj) {
for (var name in obj) { var type = obj;
if (name === "close") { while (type && type !== Object.prototype) {
// Not an RPC method. Called when the capability has no clients. Object.getOwnPropertyNames(type).forEach(name => {
this.close = obj.close.bind(obj); if (name === "close") {
} else { // Not an RPC method. Called when the capability has no clients.
var method = obj[name]; this.close = obj.close.bind(obj);
if (typeof method === "function") { } else {
this[name] = wrapLocalMethod(obj, method); var method = obj[name];
if (typeof method === "function") {
this[name] = wrapLocalMethod(obj, method);
}
} }
} })
type = Object.getPrototypeOf(type);
} }
} }
@@ -298,5 +302,3 @@ exports.bytesToPreorder = function(schema, buf, options) {
if (v8capnp.matchPowerboxQuery) exports.matchPowerboxQuery = v8capnp.matchPowerboxQuery; if (v8capnp.matchPowerboxQuery) exports.matchPowerboxQuery = v8capnp.matchPowerboxQuery;
if (v8capnp.chacha20) exports.chacha20 = v8capnp.chacha20; if (v8capnp.chacha20) exports.chacha20 = v8capnp.chacha20;
exports.dumpLocalCapTypeCounts = v8capnp.dumpLocalCapTypeCounts;
+1
View File
@@ -0,0 +1 @@
module.exports = require("capnp").importSystem("capnp/stream.capnp");