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 static char *
22 function_name(const char *lambda)
23 {
24 char *fn;
25 size_t len;
26
27 len = snprintf(NULL, 0, METHOD_NAME_FMT,
28 v8plus_js_class_name, lambda);
29 if ((fn = (char *)malloc(len + 1)) == NULL)
30 v8plus_panic("out of memory for function name for %s", lambda);
31
32 (void) snprintf(fn, len + 1, METHOD_NAME_FMT,
33 v8plus_js_class_name, lambda);
34
35 return (fn);
36 }
37
38 void
39 v8plus::ObjectWrap::init(v8::Handle<v8::Object> target)
40 {
41 uint_t i;
42
43 if (v8plus_static_method_count > 0) {
44 const v8plus_static_descr_t *sdp;
45
46 _stbl = new (std::nothrow)
47 v8plus_static_descr_t[v8plus_static_method_count];
48 if (_stbl == NULL)
49 v8plus_panic("out of memory for static method table");
50
51 for (i = 0; i < v8plus_static_method_count; i++) {
52 v8::Local<v8::FunctionTemplate> fth =
53 v8::FunctionTemplate::New(_static_entry);
54 v8::Local<v8::Function> fh = fth->GetFunction();
55 sdp = &v8plus_static_methods[i];
56
57 _stbl[i].sd_name = function_name(sdp->sd_name);
58 _stbl[i].sd_c_func = sdp->sd_c_func;
59
60 fh->SetName(v8::String::New(_stbl[i].sd_name));
61
62 target->Set(v8::String::NewSymbol(sdp->sd_name), fh);
63 }
64 }
65
66
67 if (v8plus_method_count > 0) {
68 v8::Local<v8::FunctionTemplate> tpl =
69 v8::FunctionTemplate::New(_new);
70 const v8plus_method_descr_t *mdp;
71
72 _mtbl = new (std::nothrow)
73 v8plus_method_descr_t[v8plus_method_count];
74 if (_mtbl == NULL)
75 v8plus_panic("out of memory for method table");
76
77 tpl->SetClassName(v8::String::NewSymbol(v8plus_js_class_name));
78 tpl->InstanceTemplate()->SetInternalFieldCount(
79 v8plus_method_count);
80
81 for (i = 0; i < v8plus_method_count; i++) {
82 v8::Local<v8::FunctionTemplate> fth =
83 v8::FunctionTemplate::New(_entry);
84 v8::Local<v8::Function> fh = fth->GetFunction();
85 mdp = &v8plus_methods[i];
86
87 _mtbl[i].md_name = function_name(mdp->md_name);
88 _mtbl[i].md_c_func = mdp->md_c_func;
89
90 fh->SetName(v8::String::New(_mtbl[i].md_name));
91
92 tpl->PrototypeTemplate()->Set(
93 v8::String::NewSymbol(mdp->md_name), fh);
94 }
95
96 _constructor =
97 v8::Persistent<v8::Function>::New(tpl->GetFunction());
98
99 target->Set(v8::String::NewSymbol(v8plus_js_factory_name),
100 v8::FunctionTemplate::New(
101 v8plus::ObjectWrap::cons)->GetFunction());
102 }
103
104 v8plus_crossthread_init();
105 }
106
107 v8::Handle<v8::Value>
108 v8plus::ObjectWrap::_new(const v8::Arguments &args)
109 {
110 v8::HandleScope scope;
111 v8plus::ObjectWrap *op = new v8plus::ObjectWrap();
112 nvlist_t *c_excp;
113 nvlist_t *c_args;
114
115 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
116 return (V8PLUS_THROW_DEFAULT());
117
118 c_excp = v8plus_ctor(c_args, &op->_c_impl);
119 nvlist_free(c_args);
120 if (op->_c_impl == NULL) {
121 if (c_excp == NULL) {
122 return (V8PLUS_THROW_DEFAULT());
123 } else {
124 return (V8PLUS_THROW_DECORATED(c_excp));
125 }
126 }
127
128 _objhash.insert(std::make_pair(op->_c_impl, op));
129 op->Wrap(args.This());
130
131 return (args.This());
132 }
133
134 v8plus::ObjectWrap::~ObjectWrap()
135 {
136 v8plus_dtor(_c_impl);
137 (void) _objhash.erase(_c_impl);
138 }
139
140 v8::Handle<v8::Value>
141 v8plus::ObjectWrap::cons(const v8::Arguments &args)
142 {
143 v8::HandleScope scope;
144 const unsigned argc = 1;
145 v8::Handle<v8::Value> argv[argc] = { args[0] };
146 v8::Local<v8::Object> instance = _constructor->NewInstance(argc, argv);
147
148 return (scope.Close(instance));
149 }
150
151 v8plus::ObjectWrap *
152 v8plus::ObjectWrap::objlookup(const void *cop)
153 {
154 std::unordered_map<void *, v8plus::ObjectWrap *>::iterator it;
155
156 if ((it = _objhash.find(const_cast<void *>(cop))) == _objhash.end())
157 v8plus_panic("unable to find C++ wrapper for %p\n", cop);
158
159 return (it->second);
160 }
161
162 /*
163 * This is the entry point for all methods. We will start by demultiplexing
164 * out the C method from the function name by which we were called. There is
165 * probably some mechanism by which overly clever JavaScript code could make
166 * this not match the actual name; this will kill your Node process, so don't
167 * get cute.
168 */
169 v8::Handle<v8::Value>
170 v8plus::ObjectWrap::_entry(const v8::Arguments &args)
171 {
172 v8::HandleScope scope;
173 v8plus::ObjectWrap *op =
174 node::ObjectWrap::Unwrap<v8plus::ObjectWrap>(args.This());
175 nvlist_t *c_args;
176 nvlist_t *c_out;
177 nvlist_t *excp;
178 nvpair_t *rpp;
179 v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
180 v8::String::Utf8Value selfsv(self);
181 const char *fn = *selfsv;
182 const v8plus_method_descr_t *mdp;
183 v8plus_c_method_f c_method = NULL;
184 uint_t i;
185
186 for (i = 0; i < v8plus_method_count; i++) {
187 mdp = &_mtbl[i];
188 if (strcmp(mdp->md_name, fn) == 0) {
189 c_method = mdp->md_c_func;
190 break;
191 }
192 }
193
194 if (c_method == NULL)
195 v8plus_panic("impossible method name %s\n", fn);
196
197 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
198 return (V8PLUS_THROW_DEFAULT());
199
200 c_out = c_method(op->_c_impl, c_args);
201 nvlist_free(c_args);
202
203 if (c_out == NULL) {
204 if (_v8plus_errno == V8PLUSERR_NOERROR)
205 return (scope.Close(v8::Undefined()));
206 else
207 return (V8PLUS_THROW_DEFAULT());
208 } else {
209 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
210 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
211 nvlist_free(c_out);
212 return (x);
213 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
214 v8::Handle<v8::Value> r =
215 v8plus::nvpair_to_v8_Value(rpp);
216 nvlist_free(c_out);
217 return (scope.Close(r));
218 } else {
219 v8plus_panic("bad encoded object in return");
220 }
221 }
222
223 /*NOTREACHED*/
224 return (v8::Undefined());
225 }
226
227 v8::Handle<v8::Value>
228 v8plus::ObjectWrap::_static_entry(const v8::Arguments &args)
229 {
230 v8::HandleScope scope;
231 nvlist_t *c_args;
232 nvlist_t *c_out;
233 nvlist_t *excp;
234 nvpair_t *rpp;
235 v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
236 v8::String::Utf8Value selfsv(self);
237 const char *fn = *selfsv;
238 const v8plus_static_descr_t *sdp;
239 v8plus_c_static_f c_static = NULL;
240 uint_t i;
241
242 for (i = 0; i < v8plus_static_method_count; i++) {
243 sdp = &_stbl[i];
244 if (strcmp(sdp->sd_name, fn) == 0) {
245 c_static = sdp->sd_c_func;
246 break;
247 }
248 }
249
250 if (c_static == NULL)
251 v8plus_panic("impossible static method name %s\n", fn);
252
253 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
254 return (V8PLUS_THROW_DEFAULT());
255
256 c_out = c_static(c_args);
257 nvlist_free(c_args);
258
259 if (c_out == NULL) {
260 if (_v8plus_errno == V8PLUSERR_NOERROR)
261 return (scope.Close(v8::Undefined()));
262 else
263 return (V8PLUS_THROW_DEFAULT());
264 } else {
265 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
266 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
267 nvlist_free(c_out);
268 return (x);
269 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
270 v8::Handle<v8::Value> r =
271 v8plus::nvpair_to_v8_Value(rpp);
272 nvlist_free(c_out);
273 return (scope.Close(r));
274 } else {
275 v8plus_panic("bad encoded object in return");
276 }
277 }
278
279 /*NOTREACHED*/
280 return (v8::Undefined());
281 }
282
283 v8::Handle<v8::Value>
284 v8plus::ObjectWrap::call(const char *name,
285 int argc, v8::Handle<v8::Value> argv[])
286 {
287 v8::Handle<v8::Value> v = v8::Undefined();
288 v8::Local<v8::Value> f = handle_->Get(v8::String::NewSymbol(name));
289
290 /*
291 * XXX - we'd like to throw here, but for some reason our TryCatch
292 * block doesn't seem to handle the exception.
293 */
294 if (!f->IsFunction())
295 return (v8::Undefined());
296
297 #ifdef NODE_MAKECALLBACK_RETURN
298 v =
299 #endif
300 node::MakeCallback(handle_, name, argc, argv);
301
302 return (v);
303 }
304
305 void
306 v8plus::ObjectWrap::public_Ref(void)
307 {
308 this->Ref();
309 }
310
311 void
312 v8plus::ObjectWrap::public_Unref(void)
313 {
314 this->Unref();
315 }
316
317 extern "C" void
318 init(v8::Handle<v8::Object> target)
319 {
320 v8plus::ObjectWrap::init(target);
321 }