1 /*
2 * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 */
4
5 #include <sys/types.h>
6 #include <string.h>
7 #include <new>
8 #include <unordered_map>
9 #include <stdlib.h>
10 #include <node.h>
11 #include "v8plus_impl.h"
12 #include "v8plus_glue.h"
13
14 #define METHOD_NAME_FMT "__v8plus_%s_%s"
15
16 v8::Persistent<v8::Function> v8plus::ObjectWrap::_constructor;
17 v8plus_method_descr_t *v8plus::ObjectWrap::_mtbl;
18 v8plus_static_descr_t *v8plus::ObjectWrap::_stbl;
19 std::unordered_map<void *, v8plus::ObjectWrap *> v8plus::ObjectWrap::_objhash;
20
21 uv_async_t v8plus::ObjectWrap::_uv_async;
22 pthread_mutex_t v8plus::ObjectWrap::_callq_mutex;
23 std::queue<v8plus_async_call_t *> v8plus::ObjectWrap::_callq;
24 boolean_t v8plus::ObjectWrap::_crossthread_init_done = _B_FALSE;
25 unsigned long v8plus::ObjectWrap::_uv_event_thread;
26
27 static char *
28 function_name(const char *lambda)
29 {
30 char *fn;
31 size_t len;
32
33 len = snprintf(NULL, 0, METHOD_NAME_FMT,
34 v8plus_js_class_name, lambda);
35 if ((fn = (char *)malloc(len + 1)) == NULL)
36 v8plus_panic("out of memory for function name for %s", lambda);
37
38 (void) snprintf(fn, len + 1, METHOD_NAME_FMT,
39 v8plus_js_class_name, lambda);
40
41 return (fn);
42 }
43
44 void
45 v8plus::ObjectWrap::init(v8::Handle<v8::Object> target)
46 {
47 uint_t i;
48
49 if (v8plus_static_method_count > 0) {
50 const v8plus_static_descr_t *sdp;
51
52 _stbl = new (std::nothrow)
53 v8plus_static_descr_t[v8plus_static_method_count];
54 if (_stbl == NULL)
55 v8plus_panic("out of memory for static method table");
56
57 for (i = 0; i < v8plus_static_method_count; i++) {
58 v8::Local<v8::FunctionTemplate> fth =
59 v8::FunctionTemplate::New(_static_entry);
60 v8::Local<v8::Function> fh = fth->GetFunction();
61 sdp = &v8plus_static_methods[i];
62
63 _stbl[i].sd_name = function_name(sdp->sd_name);
64 _stbl[i].sd_c_func = sdp->sd_c_func;
65
66 fh->SetName(v8::String::New(_stbl[i].sd_name));
67
68 target->Set(v8::String::NewSymbol(sdp->sd_name), fh);
69 }
70 }
71
72
73 if (v8plus_method_count > 0) {
74 v8::Local<v8::FunctionTemplate> tpl =
75 v8::FunctionTemplate::New(_new);
76 const v8plus_method_descr_t *mdp;
77
78 _mtbl = new (std::nothrow)
79 v8plus_method_descr_t[v8plus_method_count];
80 if (_mtbl == NULL)
81 v8plus_panic("out of memory for method table");
82
83 tpl->SetClassName(v8::String::NewSymbol(v8plus_js_class_name));
84 tpl->InstanceTemplate()->SetInternalFieldCount(
85 v8plus_method_count);
86
87 for (i = 0; i < v8plus_method_count; i++) {
88 v8::Local<v8::FunctionTemplate> fth =
89 v8::FunctionTemplate::New(_entry);
90 v8::Local<v8::Function> fh = fth->GetFunction();
91 mdp = &v8plus_methods[i];
92
93 _mtbl[i].md_name = function_name(mdp->md_name);
94 _mtbl[i].md_c_func = mdp->md_c_func;
95
96 fh->SetName(v8::String::New(_mtbl[i].md_name));
97
98 tpl->PrototypeTemplate()->Set(
99 v8::String::NewSymbol(mdp->md_name), fh);
100 }
101
102 _constructor =
103 v8::Persistent<v8::Function>::New(tpl->GetFunction());
104
105 target->Set(v8::String::NewSymbol(v8plus_js_factory_name),
106 v8::FunctionTemplate::New(
107 v8plus::ObjectWrap::cons)->GetFunction());
108 }
109 }
110
111 v8::Handle<v8::Value>
112 v8plus::ObjectWrap::_new(const v8::Arguments &args)
113 {
114 v8::HandleScope scope;
115 v8plus::ObjectWrap *op = new v8plus::ObjectWrap();
116 nvlist_t *c_excp;
117 nvlist_t *c_args;
118
119 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
120 return (V8PLUS_THROW_DEFAULT());
121
122 if (_crossthread_init_done == _B_FALSE) {
123 /*
124 * Initialise structures for off-event-loop method calls.
125 *
126 * Note that uv_async_init() must be called inside the libuv
127 * Event Loop, so we do it here. We also want to record the
128 * thread ID of the Event Loop thread so as to determine what
129 * kind of method calls to make later.
130 */
131 _uv_event_thread = pthread_self();
132 if (uv_async_init(uv_default_loop(), &_uv_async,
133 v8plus_async_callback) != 0)
134 v8plus_panic("unable to initialise uv_async_t");
135 if (pthread_mutex_init(&_callq_mutex, NULL) != 0)
136 v8plus_panic("unable to initialise mutex");
137 _crossthread_init_done = _B_TRUE;
138 }
139
140 c_excp = v8plus_ctor(c_args, &op->_c_impl);
141 nvlist_free(c_args);
142 if (op->_c_impl == NULL) {
143 if (c_excp == NULL) {
144 return (V8PLUS_THROW_DEFAULT());
145 } else {
146 return (V8PLUS_THROW_DECORATED(c_excp));
147 }
148 }
149
150 _objhash.insert(std::make_pair(op->_c_impl, op));
151 op->Wrap(args.This());
152
153 return (args.This());
154 }
155
156 v8plus::ObjectWrap::~ObjectWrap()
157 {
158 v8plus_dtor(_c_impl);
159 (void) _objhash.erase(_c_impl);
160 }
161
162 v8::Handle<v8::Value>
163 v8plus::ObjectWrap::cons(const v8::Arguments &args)
164 {
165 v8::HandleScope scope;
166 const unsigned argc = 1;
167 v8::Handle<v8::Value> argv[argc] = { args[0] };
168 v8::Local<v8::Object> instance = _constructor->NewInstance(argc, argv);
169
170 return (scope.Close(instance));
171 }
172
173 v8plus::ObjectWrap *
174 v8plus::ObjectWrap::objlookup(const void *cop)
175 {
176 std::unordered_map<void *, v8plus::ObjectWrap *>::iterator it;
177
178 if ((it = _objhash.find(const_cast<void *>(cop))) == _objhash.end())
179 v8plus_panic("unable to find C++ wrapper for %p\n", cop);
180
181 return (it->second);
182 }
183
184 /*
185 * This is the entry point for all methods. We will start by demultiplexing
186 * out the C method from the function name by which we were called. There is
187 * probably some mechanism by which overly clever JavaScript code could make
188 * this not match the actual name; this will kill your Node process, so don't
189 * get cute.
190 */
191 v8::Handle<v8::Value>
192 v8plus::ObjectWrap::_entry(const v8::Arguments &args)
193 {
194 v8::HandleScope scope;
195 v8plus::ObjectWrap *op =
196 node::ObjectWrap::Unwrap<v8plus::ObjectWrap>(args.This());
197 nvlist_t *c_args;
198 nvlist_t *c_out;
199 nvlist_t *excp;
200 nvpair_t *rpp;
201 v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
202 v8::String::Utf8Value selfsv(self);
203 const char *fn = *selfsv;
204 const v8plus_method_descr_t *mdp;
205 v8plus_c_method_f c_method = NULL;
206 uint_t i;
207
208 for (i = 0; i < v8plus_method_count; i++) {
209 mdp = &_mtbl[i];
210 if (strcmp(mdp->md_name, fn) == 0) {
211 c_method = mdp->md_c_func;
212 break;
213 }
214 }
215
216 if (c_method == NULL)
217 v8plus_panic("impossible method name %s\n", fn);
218
219 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
220 return (V8PLUS_THROW_DEFAULT());
221
222 c_out = c_method(op->_c_impl, c_args);
223 nvlist_free(c_args);
224
225 if (c_out == NULL) {
226 if (_v8plus_errno == V8PLUSERR_NOERROR)
227 return (scope.Close(v8::Undefined()));
228 else
229 return (V8PLUS_THROW_DEFAULT());
230 } else {
231 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
232 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
233 nvlist_free(c_out);
234 return (x);
235 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
236 v8::Handle<v8::Value> r =
237 v8plus::nvpair_to_v8_Value(rpp);
238 nvlist_free(c_out);
239 return (scope.Close(r));
240 } else {
241 v8plus_panic("bad encoded object in return");
242 }
243 }
244
245 /*NOTREACHED*/
246 return (v8::Undefined());
247 }
248
249 v8::Handle<v8::Value>
250 v8plus::ObjectWrap::_static_entry(const v8::Arguments &args)
251 {
252 v8::HandleScope scope;
253 nvlist_t *c_args;
254 nvlist_t *c_out;
255 nvlist_t *excp;
256 nvpair_t *rpp;
257 v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
258 v8::String::Utf8Value selfsv(self);
259 const char *fn = *selfsv;
260 const v8plus_static_descr_t *sdp;
261 v8plus_c_static_f c_static = NULL;
262 uint_t i;
263
264 for (i = 0; i < v8plus_static_method_count; i++) {
265 sdp = &_stbl[i];
266 if (strcmp(sdp->sd_name, fn) == 0) {
267 c_static = sdp->sd_c_func;
268 break;
269 }
270 }
271
272 if (c_static == NULL)
273 v8plus_panic("impossible static method name %s\n", fn);
274
275 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
276 return (V8PLUS_THROW_DEFAULT());
277
278 c_out = c_static(c_args);
279 nvlist_free(c_args);
280
281 if (c_out == NULL) {
282 if (_v8plus_errno == V8PLUSERR_NOERROR)
283 return (scope.Close(v8::Undefined()));
284 else
285 return (V8PLUS_THROW_DEFAULT());
286 } else {
287 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
288 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
289 nvlist_free(c_out);
290 return (x);
291 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
292 v8::Handle<v8::Value> r =
293 v8plus::nvpair_to_v8_Value(rpp);
294 nvlist_free(c_out);
295 return (scope.Close(r));
296 } else {
297 v8plus_panic("bad encoded object in return");
298 }
299 }
300
301 /*NOTREACHED*/
302 return (v8::Undefined());
303 }
304
305 v8::Handle<v8::Value>
306 v8plus::ObjectWrap::call(const char *name,
307 int argc, v8::Handle<v8::Value> argv[])
308 {
309 v8::Handle<v8::Value> v = v8::Undefined();
310 v8::Local<v8::Value> f = handle_->Get(v8::String::NewSymbol(name));
311
312 /*
313 * XXX - we'd like to throw here, but for some reason our TryCatch
314 * block doesn't seem to handle the exception.
315 */
316 if (!f->IsFunction())
317 return (v8::Undefined());
318
319 #ifdef NODE_MAKECALLBACK_RETURN
320 v =
321 #endif
322 node::MakeCallback(handle_, name, argc, argv);
323
324 return (v);
325 }
326
327 void
328 v8plus::ObjectWrap::public_Ref(void)
329 {
330 this->Ref();
331 }
332
333 void
334 v8plus::ObjectWrap::public_Unref(void)
335 {
336 this->Unref();
337 }
338
339 boolean_t
340 v8plus::ObjectWrap::in_event_thread(void)
341 {
342 if (_crossthread_init_done != _B_TRUE)
343 v8plus_panic("cross thread call init not done!");
344
345 return (_uv_event_thread == pthread_self() ? _B_TRUE : _B_FALSE);
346 }
347
348 v8plus_async_call_t *
349 v8plus::ObjectWrap::next_async_call(void)
350 {
351 v8plus_async_call_t *ret = NULL;
352
353 if (pthread_mutex_lock(&_callq_mutex) != 0)
354 v8plus_panic("could not lock callq mutex");
355
356 if (!_callq.empty()) {
357 ret = _callq.front();
358 _callq.pop();
359 }
360
361 if (pthread_mutex_unlock(&_callq_mutex) != 0)
362 v8plus_panic("could not release callq mutex");
363
364 return (ret);
365 }
366
367 void
368 v8plus::ObjectWrap::post_async_call(v8plus_async_call_t *ac)
369 {
370 if (pthread_mutex_lock(&_callq_mutex) != 0)
371 v8plus_panic("could not lock callq mutex");
372
373 _callq.push(ac);
374
375 if (pthread_mutex_unlock(&_callq_mutex) != 0)
376 v8plus_panic("could not release callq mutex");
377
378 uv_async_send(&_uv_async);
379 }
380
381 extern "C" void
382 init(v8::Handle<v8::Object> target)
383 {
384 v8plus::ObjectWrap::init(target);
385 }