Skip to content

Commit 14599d9

Browse files
committed
Add context object to pass data from JavaScript to PHP.
1 parent f4b0bf6 commit 14599d9

File tree

9 files changed

+346
-113
lines changed

9 files changed

+346
-113
lines changed

lib/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ exports.request = function(options, cb) {
2525
source = 'require '+addslashes(options.file)+';';
2626
}
2727
var stream = options.stream || process.stdout;
28-
return request(source, stream).tap(function() {
28+
var context = options.context;
29+
return request(source, stream, context).tap(function() {
2930
// ensure the stream is flushed before promise is resolved
3031
return new Promise(function(resolve, reject) {
3132
stream.write(new Buffer(0), resolve);

src/asyncmessageworker.h

Lines changed: 138 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#ifndef ASYNCMESSAGEWORKER_H
22
#define ASYNCMESSAGEWORKER_H
33
#include <nan.h>
4+
#include <cassert>
45
#include <list>
6+
#include <unordered_map>
57
#include "messages.h"
8+
#include "node_php_jsobject_class.h"
69

710
namespace node_php_embed {
811

@@ -11,42 +14,117 @@ namespace node_php_embed {
1114
*/
1215
/* abstract */ class AsyncMessageWorker : public Nan::AsyncWorker {
1316
public:
14-
explicit AsyncMessageWorker(Nan::Callback *callback_)
15-
: AsyncWorker(callback_), asyncdata_(), waitingForLock_(false),
16-
nextId_(0) {
17+
class MessageChannel;
18+
explicit AsyncMessageWorker(Nan::Callback *callback)
19+
: AsyncWorker(callback), asyncdata_(), waitingForLock_(false),
20+
phpObjToId_(), phpObjList_(),
21+
// id #0 is reserved for "invalid object"
22+
nextId_(1) {
1723
async = new uv_async_t;
1824
uv_async_init(uv_default_loop(), async, AsyncMessage_);
1925
async->data = this;
2026

21-
uv_mutex_init(&async_lock);
27+
uv_mutex_init(&async_lock_);
28+
uv_mutex_init(&id_lock_);
2229

23-
objToId_.Reset(v8::NativeWeakMap::New(v8::Isolate::GetCurrent()));
30+
jsObjToId_.Reset(v8::NativeWeakMap::New(v8::Isolate::GetCurrent()));
2431
}
2532

2633
virtual ~AsyncMessageWorker() {
27-
uv_mutex_destroy(&async_lock);
34+
// invalidate any lingering js wrapper objects to this request
35+
ClearAllJsIds();
36+
uv_mutex_destroy(&async_lock_);
37+
uv_mutex_destroy(&id_lock_);
2838
// can't safely delete entries from asyncdata_, it better be empty.
29-
objToId_.Reset();
39+
jsObjToId_.Reset();
3040
}
3141

32-
// Map Js object to an index
33-
uint32_t IdForJsObj(const v8::Local<v8::Object> o) {
42+
// Map Js object to an index (JS thread only)
43+
objid_t IdForJsObj(const v8::Local<v8::Object> o) {
3444
// Have we already mapped this?
3545
Nan::HandleScope scope;
36-
v8::Local<v8::NativeWeakMap> objToId = Nan::New(objToId_);
37-
if (objToId->Has(o)) {
38-
return Nan::To<uint32_t>(objToId->Get(o)).FromJust();
46+
v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New(jsObjToId_);
47+
if (jsObjToId->Has(o)) {
48+
return Nan::To<objid_t>(jsObjToId->Get(o)).FromJust();
49+
}
50+
uv_mutex_lock(&id_lock_);
51+
objid_t id = (nextId_++);
52+
uv_mutex_unlock(&id_lock_);
53+
jsObjToId->Set(o, Nan::New(id));
54+
SaveToPersistent(id, o);
55+
return id;
56+
}
57+
v8::Local<v8::Object> JsObjForId(objid_t id) {
58+
Nan::EscapableHandleScope scope;
59+
// XXX if doesn't exist, then make a wrapper (and store it in the maps)
60+
return scope.Escape(Nan::To<v8::Object>(
61+
GetFromPersistent(id)
62+
).ToLocalChecked());
63+
}
64+
// Map PHP object to an index (PHP thread only)
65+
objid_t IdForPhpObj(zval *z) {
66+
if (phpObjToId_.count(z)) {
67+
return phpObjToId_.at(z);
68+
}
69+
uv_mutex_lock(&id_lock_);
70+
objid_t id = (nextId_++);
71+
uv_mutex_unlock(&id_lock_);
72+
Z_ADDREF_P(z);
73+
if (id >= phpObjList_.size()) { phpObjList_.resize(id+1); }
74+
phpObjList_[id] = z;
75+
phpObjToId_[z] = id;
76+
return id;
77+
}
78+
// returned value is owned by objectmapper, caller should not release it.
79+
zval * PhpObjForId(MessageChannel *channel, objid_t id TSRMLS_DC) {
80+
if (id >= phpObjList_.size()) { phpObjList_.resize(id+1); }
81+
ZVal z(phpObjList_[id] ZEND_FILE_LINE_CC);
82+
if (z.IsNull()) {
83+
node_php_jsobject_create(z.Ptr(), channel, id TSRMLS_CC);
84+
phpObjList_[id] = z.Ptr();
85+
phpObjToId_[z.Ptr()] = id;
86+
// one excess reference: owned by objectmapper
87+
return z.Escape();
88+
}
89+
// don't increment reference
90+
return z.Ptr();
91+
}
92+
// Free PHP references associated with an id (from PHP thread)
93+
void ClearPhpId(objid_t id) {
94+
zval *z = (id < phpObjList_.size()) ? phpObjList_[id] : NULL;
95+
if (z) {
96+
phpObjList_[id] = NULL;
97+
phpObjToId_.erase(z);
98+
zval_ptr_dtor(&z);
99+
}
100+
}
101+
void ClearAllPhpIds() {
102+
for (objid_t id = 1; id < nextId_; id++) {
103+
ClearPhpId(id);
104+
}
105+
}
106+
// Free JS references associated with an id (from JS thread)
107+
void ClearJsId(objid_t id) {
108+
Nan::HandleScope scope;
109+
Nan::MaybeLocal<v8::Object> o =
110+
Nan::To<v8::Object>(GetFromPersistent(id));
111+
if (o.IsEmpty()) { return; }
112+
// XXX depending on o's type, set its "is request valid" flag to false
113+
// since there might be other references to this object.
114+
v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New(jsObjToId_);
115+
jsObjToId->Delete(o.ToLocalChecked());
116+
SaveToPersistent(id, Nan::Undefined());
117+
}
118+
void ClearAllJsIds() {
119+
for (objid_t id = 1; id < nextId_; id++) {
120+
ClearJsId(id);
39121
}
40-
uint32_t r = (nextId_++);
41-
objToId->Set(o, Nan::New(r));
42-
SaveToPersistent(r, o);
43-
return r;
44122
}
45123

46124
void WorkComplete() {
47-
uv_mutex_lock(&async_lock);
125+
uv_mutex_lock(&async_lock_);
48126
waitingForLock_ = !asyncdata_.empty();
49-
uv_mutex_unlock(&async_lock);
127+
uv_mutex_unlock(&async_lock_);
50128

51129
if (!waitingForLock_) {
52130
Nan::AsyncWorker::WorkComplete();
@@ -62,10 +140,10 @@ namespace node_php_embed {
62140
std::list<MessageToJs *> newData;
63141
bool waiting;
64142

65-
uv_mutex_lock(&async_lock);
143+
uv_mutex_lock(&async_lock_);
66144
newData.splice(newData.begin(), asyncdata_);
67145
waiting = waitingForLock_;
68-
uv_mutex_unlock(&async_lock);
146+
uv_mutex_unlock(&async_lock_);
69147

70148
for (std::list<MessageToJs *>::iterator it = newData.begin();
71149
it != newData.end(); it++) {
@@ -91,20 +169,17 @@ namespace node_php_embed {
91169
AsyncMessageWorker *GetWorker() const {
92170
return that_;
93171
}
94-
virtual uint32_t IdForJsObj(const v8::Local<v8::Object> o) {
172+
virtual objid_t IdForJsObj(const v8::Local<v8::Object> o) {
95173
return that_->IdForJsObj(o);
96174
}
97-
virtual v8::Local<v8::Object> GetJs(uint32_t id) {
98-
Nan::EscapableHandleScope scope;
99-
return scope.Escape(Nan::To<v8::Object>(
100-
that_->GetFromPersistent(id)
101-
).ToLocalChecked());
175+
virtual v8::Local<v8::Object> JsObjForId(objid_t id) {
176+
return that_->JsObjForId(id);
102177
}
103-
virtual uint32_t IdForPhpObj(const zval *o) {
104-
return 0; // XXX
178+
virtual objid_t IdForPhpObj(zval *o) {
179+
return that_->IdForPhpObj(o);
105180
}
106-
virtual void GetPhp(uint32_t id, zval *return_value TSRMLS_DC) {
107-
// XXX use GetFromPersistent, and then unwrap v8::External?
181+
virtual zval * PhpObjForId(objid_t id TSRMLS_DC) {
182+
return that_->PhpObjForId(this, id TSRMLS_CC);
108183
}
109184
private:
110185
explicit MessageChannel(AsyncMessageWorker* that) : that_(that) {}
@@ -123,16 +198,37 @@ namespace node_php_embed {
123198
* WorkComplete */
124199
}
125200

201+
// Limited ObjectMapper for use during subclass initialization.
202+
protected:
203+
class JsOnlyMapper : public ObjectMapper {
204+
public:
205+
JsOnlyMapper(AsyncMessageWorker *worker) : worker_(worker) { }
206+
virtual objid_t IdForJsObj(const v8::Local<v8::Object> o) {
207+
return worker_->IdForJsObj(o);
208+
}
209+
virtual v8::Local<v8::Object> JsObjForId(objid_t id) {
210+
assert(false); return Nan::New<v8::Object>();
211+
}
212+
virtual objid_t IdForPhpObj(zval *o) {
213+
assert(false); return 0;
214+
}
215+
virtual zval * PhpObjForId(objid_t id TSRMLS_DC) {
216+
assert(false); return NULL;
217+
}
218+
private:
219+
AsyncMessageWorker *worker_;
220+
};
221+
126222
private:
127223
void Execute() /*final override*/ {
128224
MessageChannel messageChannel(this);
129225
Execute(messageChannel);
130226
}
131227

132228
void SendMessage_(MessageToJs *b) {
133-
uv_mutex_lock(&async_lock);
229+
uv_mutex_lock(&async_lock_);
134230
asyncdata_.push_back(b);
135-
uv_mutex_unlock(&async_lock);
231+
uv_mutex_unlock(&async_lock_);
136232

137233
uv_async_send(async);
138234
}
@@ -151,12 +247,19 @@ namespace node_php_embed {
151247
}
152248

153249
uv_async_t *async;
154-
uv_mutex_t async_lock;
250+
uv_mutex_t async_lock_;
155251
std::list<MessageToJs *> asyncdata_;
156252
bool waitingForLock_;
157253
// Js Object mapping (along with GetFromPersistent/etc)
158-
Nan::Persistent<v8::NativeWeakMap> objToId_;
159-
uint32_t nextId_;
254+
// Read/writable only from Js thread
255+
Nan::Persistent<v8::NativeWeakMap> jsObjToId_;
256+
// PHP Object mapping
257+
// Read/writable only from PHP thread
258+
std::unordered_map<zval*,objid_t> phpObjToId_;
259+
std::vector<zval*> phpObjList_;
260+
// Ids are allocated from both threads, so mutex is required
261+
uv_mutex_t id_lock_;
262+
objid_t nextId_;
160263
};
161264

162265
}

src/messages.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,8 @@ class JsInvokeMethodMsg : public MessageToJs {
106106
public:
107107
JsInvokeMethodMsg(ObjectMapper *m, zval *obj, zval *name, int argc, zval **argv)
108108
: MessageToJs(m), obj_(m, obj), name_(m, name), argc_(argc), argv_(Value::NewArray(m, argc, argv)) { }
109-
JsInvokeMethodMsg(ObjectMapper *m, uint32_t objId, const char *name, int argc, zval **argv)
110-
: MessageToJs(m), obj_(), name_(), argc_(argc), argv_(Value::NewArray(m, argc, argv)) {
111-
obj_.SetJsObject(objId);
109+
JsInvokeMethodMsg(ObjectMapper *m, zval *obj, const char *name, int argc, zval **argv)
110+
: MessageToJs(m), obj_(m, obj), name_(), argc_(argc), argv_(Value::NewArray(m, argc, argv)) {
112111
name_.SetOwnedString(name, strlen(name));
113112
}
114113
virtual ~JsInvokeMethodMsg() { delete[] argv_; }
@@ -149,12 +148,12 @@ class PhpGetPropertyMsg : public MessageToPhp {
149148
: MessageToPhp(m), obj_(m, obj), name_(m, name) { }
150149
protected:
151150
virtual void InPhp(ObjectMapper *m TSRMLS_DC) {
152-
ZVal obj, name;
151+
ZVal obj(ZEND_FILE_LINE_C), name(ZEND_FILE_LINE_C);
153152
zval *r;
154153
zend_class_entry *ce;
155154
zend_property_info *property_info;
156155

157-
obj_.ToPhp(m, *obj TSRMLS_CC); name_.ToPhp(m, *name TSRMLS_CC);
156+
obj_.ToPhp(m, obj TSRMLS_CC); name_.ToPhp(m, name TSRMLS_CC);
158157
if (!(obj.IsObject() && name.IsString())) {
159158
retval_.SetNull();
160159
return;

0 commit comments

Comments
 (0)