Skip to content

Commit 90dc026

Browse files
committed
Implement __call
This undoes the hack we were using to pass a Buffer to the output stream; we'll have to reimplement that "properly".
1 parent fb3e921 commit 90dc026

File tree

3 files changed

+92
-81
lines changed

3 files changed

+92
-81
lines changed

src/messages.h

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -79,70 +79,7 @@ class JsMessageChannel : public ObjectMapper {
7979
virtual void Send(MessageToJs *m) const = 0;
8080
};
8181

82-
class JsGetPropertyMsg : public MessageToJs {
83-
Value obj_;
84-
Value name_;
85-
public:
86-
JsGetPropertyMsg(ObjectMapper *m, zval *obj, zval *name)
87-
: MessageToJs(m), obj_(m, obj), name_(m, name) { }
88-
protected:
89-
virtual void InJs(ObjectMapper *m) {
90-
Nan::MaybeLocal<v8::Object> o = Nan::To<v8::Object>(obj_.ToJs(m));
91-
if (o.IsEmpty()) {
92-
return Nan::ThrowTypeError("not an object");
93-
}
94-
Nan::MaybeLocal<v8::Value> r =
95-
Nan::Get(o.ToLocalChecked(), name_.ToJs(m));
96-
if (!r.IsEmpty()) {
97-
retval_.Set(m, r.ToLocalChecked());
98-
}
99-
}
100-
};
101-
class JsInvokeMethodMsg : public MessageToJs {
102-
Value obj_;
103-
Value name_;
104-
int argc_;
105-
Value *argv_;
106-
public:
107-
JsInvokeMethodMsg(ObjectMapper *m, zval *obj, zval *name, int argc, zval **argv)
108-
: MessageToJs(m), obj_(m, obj), name_(m, name), argc_(argc), argv_(Value::NewArray(m, argc, argv)) { }
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)) {
111-
name_.SetOwnedString(name, strlen(name));
112-
}
113-
virtual ~JsInvokeMethodMsg() { delete[] argv_; }
114-
// this is a bit of a hack to allow constructing a call with a Buffer
115-
// as an argument.
116-
Value &Argv(int n) { return argv_[n]; }
117-
protected:
118-
virtual void InJs(ObjectMapper *m) {
119-
Nan::MaybeLocal<v8::Object> o = Nan::To<v8::Object>(obj_.ToJs(m));
120-
if (o.IsEmpty()) {
121-
return Nan::ThrowTypeError("not an object");
122-
}
123-
Nan::MaybeLocal<v8::Object> method = Nan::To<v8::Object>(
124-
Nan::Get(o.ToLocalChecked(), name_.ToJs(m))
125-
.FromMaybe<v8::Value>(Nan::Undefined())
126-
);
127-
if (method.IsEmpty()) {
128-
return Nan::ThrowTypeError("method is not an object");
129-
}
130-
v8::Local<v8::Value> *argv =
131-
static_cast<v8::Local<v8::Value>*>
132-
(alloca(sizeof(v8::Local<v8::Value>) * argc_));
133-
for (int i=0; i<argc_; i++) {
134-
new(&argv[i]) v8::Local<v8::Value>;
135-
argv[i] = argv_[i].ToJs(m);
136-
}
137-
Nan::MaybeLocal<v8::Value> result =
138-
Nan::CallAsFunction(method.ToLocalChecked(), o.ToLocalChecked(),
139-
argc_, argv);
140-
if (!result.IsEmpty()) {
141-
retval_.Set(m, result.ToLocalChecked());
142-
}
143-
}
144-
};
145-
82+
// example of MessageToPhp
14683
class PhpGetPropertyMsg : public MessageToPhp {
14784
Value obj_;
14885
Value name_;

src/node_php_embed.cc

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include <nan.h>
22

3+
extern "C" {
34
#include <sapi/embed/php_embed.h>
45
#include <Zend/zend_exceptions.h>
56
#include <ext/standard/info.h>
7+
}
68

79
#include "node_php_embed.h"
810
#include "node_php_jsobject_class.h"
@@ -113,19 +115,15 @@ static int node_php_embed_ub_write(const char *str, unsigned int str_length TSRM
113115
AsyncMessageWorker::MessageChannel *messageChannel = NODE_PHP_EMBED_G(messageChannel);
114116
PhpRequestWorker *worker = (PhpRequestWorker *)
115117
(messageChannel->GetWorker());
116-
ZVal stream{ZEND_FILE_LINE_C};
118+
ZVal stream{ZEND_FILE_LINE_C}, buf{ZEND_FILE_LINE_C},
119+
retval{ZEND_FILE_LINE_C};
117120
worker->GetStream().ToPhp(messageChannel, stream TSRMLS_CC);
118-
zval buf; INIT_ZVAL(buf); // stack allocate a null zval as a placeholder
119-
zval *args[] = { &buf };
120-
JsInvokeMethodMsg msg(messageChannel, stream.Ptr(), "write", 1, args);
121-
// hack the message to pass a buffer, not a string
122-
// (and avoid unnecessary copying by not using an "owned buffer")
123-
msg.Argv(0).SetBuffer(str, str_length);
124-
messageChannel->Send(&msg);
125-
// XXX wait for response
126-
msg.WaitForResponse(); // XXX optional?
127-
// now return value is in msg.retval_ or msg.exception_ but
128-
// we'll ignore that (FIXME?)
121+
// use plain zval to avoid allocating copy of method name
122+
zval method; ZVAL_STRINGL(&method, "write", 5, 0);
123+
// XXX need to pass 'str' as buffer, not a string, and avoid copying.
124+
buf.SetString(str, str_length, 1);
125+
call_user_function(EG(function_table), stream.PtrPtr(), &method,
126+
retval.Ptr(), 1, buf.PtrPtr() TSRMLS_CC);
129127
return str_length;
130128
}
131129

src/node_php_jsobject_class.cc

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
#include <nan.h>
44
extern "C" {
5-
#include "php.h"
6-
#include "Zend/zend.h"
7-
#include "Zend/zend_exceptions.h"
8-
#include "Zend/zend_types.h"
5+
#include <main/php.h>
6+
#include <Zend/zend.h>
7+
#include <Zend/zend_exceptions.h>
8+
#include <Zend/zend_types.h>
99
}
1010

1111
#include "node_php_jsobject_class.h"
12+
13+
#include "macros.h"
1214
#include "messages.h"
1315
#include "values.h"
14-
#include "macros.h"
1516

1617
#define USE_MAGIC_ISSET 0
1718

@@ -248,6 +249,76 @@ PHP_METHOD(JsObject, __unset) {
248249
msg.retval_.ToPhp(obj->channel, return_value, return_value_ptr TSRMLS_CC);
249250
}
250251

252+
class JsInvokeMethodMsg : public MessageToJs {
253+
Value object_;
254+
Value member_;
255+
ulong argc_;
256+
Value *argv_;
257+
public:
258+
JsInvokeMethodMsg(ObjectMapper *m, objid_t objId, zval *member, ulong argc, zval **argv)
259+
: MessageToJs(m), object_(), member_(m, member), argc_(argc), argv_(Value::NewArray(m, argc, argv)) {
260+
object_.SetJsObject(objId);
261+
}
262+
virtual ~JsInvokeMethodMsg() { delete[] argv_; }
263+
// this is a bit of a hack to allow constructing a call with a Buffer
264+
// as an argument.
265+
Value &Argv(int n) { return argv_[n]; }
266+
protected:
267+
virtual void InJs(ObjectMapper *m) {
268+
Nan::MaybeLocal<v8::Object> jsObj =
269+
Nan::To<v8::Object>(object_.ToJs(m));
270+
if (jsObj.IsEmpty()) {
271+
return Nan::ThrowTypeError("receiver is not an object");
272+
}
273+
274+
Nan::MaybeLocal<v8::Object> method = Nan::To<v8::Object>(
275+
Nan::Get(jsObj.ToLocalChecked(), member_.ToJs(m))
276+
.FromMaybe<v8::Value>(Nan::Undefined())
277+
);
278+
if (method.IsEmpty()) {
279+
return Nan::ThrowTypeError("method is not an object");
280+
}
281+
v8::Local<v8::Value> *argv =
282+
static_cast<v8::Local<v8::Value>*>
283+
(alloca(sizeof(v8::Local<v8::Value>) * argc_));
284+
for (ulong i=0; i<argc_; i++) {
285+
new(&argv[i]) v8::Local<v8::Value>;
286+
argv[i] = argv_[i].ToJs(m);
287+
}
288+
Nan::MaybeLocal<v8::Value> result =
289+
Nan::CallAsFunction(method.ToLocalChecked(), jsObj.ToLocalChecked(),
290+
argc_, argv);
291+
if (!result.IsEmpty()) {
292+
retval_.Set(m, result.ToLocalChecked());
293+
}
294+
}
295+
};
296+
297+
PHP_METHOD(JsObject, __call) {
298+
zval *member; zval *args;
299+
PARSE_PARAMS(__unset, "z/a", &member, &args);
300+
convert_to_string(member);
301+
HashTable *arrht = Z_ARRVAL_P(args);
302+
ulong argc = zend_hash_next_free_element(arrht); // maximum index
303+
zval **argv = static_cast<zval**>(alloca(sizeof(zval*) * argc));
304+
for (ulong i=0; i<argc; i++) {
305+
zval **z;
306+
if (zend_hash_index_find(arrht, i, (void**) &z)==FAILURE) {
307+
argv[i] = EG(uninitialized_zval_ptr);
308+
} else {
309+
argv[i] = *z;
310+
}
311+
}
312+
JsInvokeMethodMsg msg(obj->channel, obj->id, member, argc, argv);
313+
obj->channel->Send(&msg);
314+
msg.WaitForResponse();
315+
THROW_IF_EXCEPTION("JS exception thrown during __call of \"%*s\"",
316+
Z_STRLEN_P(member), Z_STRVAL_P(member));
317+
msg.retval_.ToPhp(obj->channel, return_value, return_value_ptr TSRMLS_CC);
318+
}
319+
320+
321+
251322

252323
/* Use (slightly thunked) versions of the has/read/write property handlers
253324
* for dimensions as well, so that $obj['foo'] acts like $obj->foo. */
@@ -364,6 +435,10 @@ ZEND_END_ARG_INFO()
364435
ZEND_BEGIN_ARG_INFO_EX(node_php_jsobject_unset_args, 0, 0, 1)
365436
ZEND_ARG_INFO(0, member)
366437
ZEND_END_ARG_INFO()
438+
ZEND_BEGIN_ARG_INFO_EX(node_php_jsobject_call_args, 0, 1/*return by ref*/, 1)
439+
ZEND_ARG_INFO(0, member)
440+
ZEND_ARG_INFO(0, args)
441+
ZEND_END_ARG_INFO()
367442

368443
static const zend_function_entry node_php_jsobject_methods[] = {
369444
PHP_ME(JsObject, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
@@ -375,6 +450,7 @@ static const zend_function_entry node_php_jsobject_methods[] = {
375450
PHP_ME(JsObject, __get, node_php_jsobject_get_args, ZEND_ACC_PUBLIC)
376451
PHP_ME(JsObject, __set, node_php_jsobject_set_args, ZEND_ACC_PUBLIC)
377452
PHP_ME(JsObject, __unset, node_php_jsobject_unset_args, ZEND_ACC_PUBLIC)
453+
PHP_ME(JsObject, __call, node_php_jsobject_call_args, ZEND_ACC_PUBLIC)
378454
ZEND_FE_END
379455
};
380456

0 commit comments

Comments
 (0)