Print this page
XXX doco cleanup
XXX first cut of crosscall
Split |
Close |
Expand all |
Collapse all |
--- old/./README.md
+++ new/./README.md
1 1 # v8+: Node.js addon C++ to C boundary layer
2 2
3 3 This layer offers a way to write at least simple Node.js addons in C without
4 4 all the horrible C++ goop you'd otherwise be expected to use. That goop
5 5 still exists, but you don't have to write it. More importantly, you can
6 6 write your module in a sane programming environment, avoiding the confusing
7 7 and error-prone C++ semantics.
8 8
9 9 ## Usage
10 10
11 11 Unlike most Node.js modules, v8+ does nothing by itself. It is intended to
12 12 be used as a build-time dependency of your native addon, providing you with
13 13 an alternate programming environment.
14 14
15 15 For full docs, read the source code.
16 16
17 17 ## Node.js Support
18 18
19 19 v8+ works with, and has been tested to some extent with, Node.js 0.6.18 and
20 20 0.8.1. It most likely works with other micro versions in the 0.6 and 0.8
21 21 series as well. Note that this does not mean you can necessarily expect an
22 22 addon built against a particular minor release of Node.js to work with any
23 23 other minor release of Node.js.
24 24
25 25 ## Building and Installing
26 26
27 27 The v8+ source code is compiled into your module directly along with your
28 28 code. There is no separate v8+ library or node module, so the v8+ source,
29 29 tools, and makefiles are required to be present at the time your module is
30 30 built. They are not required at runtime.
31 31
32 32 Normally, your addon module will depend on the v8plus package and install it
33 33 using npm. The v8+ makefiles are set up to accommodate the installation of
34 34 v8+ anywhere `node(1)` would be able to find it using `require()` if it were
35 35 a normal JavaScript module, so simply including it as a dependency in your
36 36 `package.json` will work correctly. In addition, you will need to create a
37 37 (normally trivial) makefile for your module that includes the makefiles
38 38 distributed as part of v8+. Once you have done so, it is sufficient to run
39 39 `gmake` to generate the native loadable module used by Node.js.
40 40
41 41 The overall outline for creating a v8+ module looks something like this:
42 42
43 43 1. Write the C code that does whatever your module does. Be sure to
44 44 \#include "v8plus_glue.h". Do not include any other v8+ headers.
45 45
46 46 2. Create an appropriate `package.json` file. See below for details.
47 47
48 48 3. Create a skeleton makefile. See below for details.
49 49
50 50 4. Create a JSON file defining the error codes your module will use. See
51 51 Errors below.
52 52
53 53 You should not (and need not) modify either of the delivered makefiles;
54 54 override the definitions in Makefile.v8plus.defs in your makefile as
55 55 appropriate.
56 56
57 57 ### Packaging Considerations
58 58
59 59 There are two essential properties your `package.json` must contain in order
60 60 to use v8+ with npm:
61 61
62 62 1. A dependency on `v8plus`.
63 63
64 64 2. An appropriate script entry for building your module. It is strongly
65 65 recommended that you use something like the following:
66 66
67 67 "postinstall": "gmake $(eval echo ${MAKE_OVERRIDES})"
68 68
69 69 This will allow someone building your module to set make variables by adding
70 70 them to the `MAKE_OVERRIDES` environment variable; e.g.,
71 71
72 72 $ MAKE_OVERRIDES="CTFCONVERT=/bin/true CTFMERGE=/bin/true" npm install
73 73
74 74 ### Tying into the Makefiles
75 75
76 76 The makefiles shipped with v8+ do the great majority of the heavy lifting
77 77 for you. A minimally functional makefile for your addon must contain four
78 78 things:
79 79
80 80 1. Variable definitions for `V8PLUS` and `PREFIX_NODE`. Alternately, you
81 81 may choose to provide these on the command line or via the environment.
82 82 It is recommended that these assignments be made exactly as follows,
83 83 which will cause the addon to be built against the `node` that is found
84 84 first in your path:
85 85
86 86 PREFIX_NODE := $(shell dirname `bash -c 'hash node; hash -t node'`)/..
87 87 V8PLUS := $(shell $(PREFIX_NODE)/bin/node -e 'require("v8plus");')
88 88
89 89 Note that the mechanism for finding `node` will not work correctly if
90 90 yours is a symlink. This invocation of node(1) uses a v8+ mechanism to
91 91 locate v8+ sources anywhere that node(1) can find them and should not be
92 92 modified unless you want to test an alternate v8+.
93 93
94 94 2. The exact line:
95 95
96 96 include $(V8PLUS)/Makefile.v8plus.defs
97 97
98 98 3. Variable assignments specific to your module. In particular, you must
99 99 define `SRCS`, `MODULE`, and `ERRNO_JSON`. Additional customisation is
100 100 optional.
101 101
102 102 4. The exact line:
103 103
104 104 include $(V8PLUS)/Makefile.v8plus.targ
105 105
106 106 Additional arbitrary customisation is possible using standard makefile
107 107 syntax; most things that are useful to change already have variables defined
108 108 in `Makefile.v8plus.defs` whose values you may append to or override. For
109 109 example, you may cause additional system libraries to be linked in by
110 110 appending `-lname` to the `LIBS` variable. By default, the makefiles assume
111 111 that your sources are located in the `src` subdirectory of your module, and
112 112 that you want the sole output of the build process to be called
113 113 `$(MODULE).node` and located in the `lib` subdirectory. This can be changed
114 114 by overriding the `MODULE_DIR` variable.
115 115
116 116 A simple example makefile may be found in the `examples/` subdirectory, and
117 117 additional examples may be found in existing consumers; see Consumers below.
118 118 The GNU people also provide a good manual for make if you get really stuck;
119 119 see <http://www.gnu.org/software/make/manual/make.html>. In general,
120 120 writing the necessary makefile fragment is expected to be as easy as or
121 121 easier than the equivalent task using `node-waf` or `node-gyp`, so if you're
122 122 finding it unexpectedly difficult or complicated there's probably an easier
123 123 way.
124 124
125 125 The makefiles follow GNU make syntax; other makes may not work but patches
126 126 that correct this are generally welcome (in particular, Sun make and GNU
127 127 make have different and incompatible ways to set a variable from the output
128 128 of a shell command, and there is no way I know to accommodate both).
129 129
130 130 ### Binary Interface
131 131
132 132 By default, the resulting object is linked with the `-zdefs` option, which
133 133 will cause the build to fail if any unresolved symbols remain. In order to
134 134 accommodate this, a mapfile specifying the available global symbols in your
135 135 `node` binary is automatically generated as part of the build process. This
136 136 makes it much easier to debug missing libraries; otherwise, a module with
137 137 unresolved symbols will fail to load at runtime with no useful explanation.
138 138 Mapfile generation probably works only on illumos-derived systems. Patches
139 139 that add support for other linkers are welcome.
140 140
141 141 Your module will have all symbols (other than `init`, which is used directly
142 142 by Node.js) reduced to local visibility, which is strongly recommended. If
143 143 for some reason you want your module's symbols to be visible to Node.js or
144 144 to other modules, you will have to modify the script that generates the
145 145 mapfile. See the `$(MAPFILE)` target in `Makefile.v8plus.targ`.
146 146
147 147 ## API
148 148
149 149 Your module is an object factory that instantiates and returns native
150 150 objects, to which a fixed set of methods is attached as properties. The
151 151 constructor, destructor, and methods all correspond 1-1 with C functions.
152 152 In addition, you may create additional class methods associated with the
153 153 native module itself, each of which will also have a 1-1 relationship to a
154 154 set of C functions.
155 155
156 156 This functionality is generally sufficient to interface with the system in
157 157 useful ways, but it is by no means exhaustive. Architectural limitations
158 158 are noted throughout the documentation.
159 159
160 160 Subsequent sections describe the API in greater detail, along with most of
161 161 the C functions that v8+ provides. Some utility functions may not be listed
162 162 here; see `v8plus_glue.h` for additional commentary and functions that are
163 163 available to you.
164 164
165 165 ### Constructors, Methods, and Functions
166 166
167 167 The interface between your module and v8+ consists of a handful of objects
168 168 with fixed types and names. These are:
169 169
170 170 const v8plus_c_ctor_f v8plus_ctor = my_ctor;
171 171 const v8plus_c_dtor_f v8plus_dtor = my_dtor;
172 172 const char *v8plus_js_factory_name = "_new";
173 173 const char *v8plus_js_class_name = "MyObjectBinding";
174 174 const v8plus_method_descr_t v8plus_methods[] = {
175 175 {
176 176 md_name: "_my_method",
177 177 md_c_func: my_method
178 178 },
179 179 ...
180 180 };
181 181 const uint_t v8plus_method_count =
182 182 sizeof (v8plus_methods) / sizeof (v8plus_methods[0]);
183 183
184 184 const v8plus_static_descr_t v8plus_static_methods[] = {
185 185 {
186 186 sd_name: "_my_function",
187 187 sd_c_func: my_function
188 188 },
189 189 ...
190 190 };
191 191 const uint_t v8plus_static_method_count =
192 192 sizeof (v8plus_static_methods) / sizeof (v8plus_static_methods[0]);
193 193
194 194 All of these must be present even if they have zero length or are NULL. The
195 195 prototypes and semantics of each function type are as follows:
196 196
197 197 ### nvlist_t *v8plus_c_ctor_f(const nvlist_t *ap, void **opp)
198 198
199 199 The constructor is responsible for creating the C object corresponding to
200 200 the native JavaScript object being created. It is not a true constructor in
201 201 that you are actually an object factory; the C++ function associated with
202 202 the JavaScript constructor is called for you. Your encoded arguments are in
203 203 `ap`. Allocate and populate a C object, stuff it into `*opp`, and return
204 204 `v8plus_void()`. If you need to throw an exception you can do so by
205 205 returning `v8plus_error()` or one of its wrappers, or by setting
206 206 `_v8plus_errno` using one of those functions and then returning an nvlist
207 207 with an `err` member representing a decorated exception.
208 208
209 209 ### void v8plus_c_dtor_f(void *op)
210 210
211 211 Free the C object `op` and anything else associated with it. Your object is
212 212 going away. This function may be empty if the constructor did not allocate
↓ open down ↓ |
212 lines elided |
↑ open up ↑ |
213 213 any memory (i.e., `op` is not a pointer to dynamically allocated memory).
214 214
215 215 ### nvlist_t *v8plus_c_method_f(void *op, const nvlist_t *ap)
216 216
217 217 When the JavaScript method is called in the context of your object, the
218 218 corresponding C function is invoked. `op` is the C object associated with
219 219 the JavaScript object, and `ap` is the encoded list of arguments to the
220 220 function. Return an encoded object with a `res` member, or use one of the
221 221 error/exception patterns.
222 222
223 -### nvlist_t *v8plus_c_static_method_f(nvlist_t *ap)
223 +### nvlist_t *v8plus_c_static_method_f(const nvlist_t *ap)
224 224
225 225 In addition to methods on the native objects returned by your constructor,
226 226 you can also provide a set of functions on the native binding object itself.
227 227 This may be useful for providing bindings to libraries for which no object
228 228 representation makes sense, or that have functions that operate outside the
229 229 context of any particular object. Your arguments are once again encoded in
230 230 `ap`, and your return values are an object containing `res` or an error.
231 231
232 232 ### Argument Handling
233 233
234 234 When JavaScript objects cross the boundary from C++ to C, they are converted
235 235 from v8 C++ objects into C nvlists. This means that they are effectively
236 236 passed by value, unlike in JavaScript or in native addons written in C++.
237 237 The arguments to the JavaScript function are treated as an array and
238 238 marshalled into a single nvlist whose properties are named "0", "1", and so
239 239 on. Each such property is encoded as follows:
240 240
241 241 - numbers and Number objects (regardless of size): double
242 242 - strings and String objects: UTF-8 encoded C string
243 243 - booleans and Boolean objects: boolean_value
244 244 - undefined: boolean
245 245 - null: byte, value 0
246 246 - Objects, including Arrays: nvlist with own properties as members and the
247 247 member ".__v8plus_type" set to the object's JavaScript type name. Note
248 248 that the member name itself begins with a . to reduce the likelihood of a
249 249 collision with an actual JavaScript member name.
250 250 - JavaScript Functions are passed in a format suitable for use with
251 251 `nvlist_lookup_jsfunc()` and `v8plus_args()` with the V8PLUS_TYPE_JSFUNC
252 252 token. This type is restricted; see below.
253 253
254 254 Because JavaScript arrays may be sparse, we cannot use the libnvpair array
255 255 types. Consider them reserved for internal use. JavaScript Arrays are
256 256 represented as they really are in JavaScript: objects with properties whose
257 257 names happen to be integers.
258 258
259 259 Other data types cannot be represented and will result in a TypeError
260 260 being thrown. If your object has methods that need other argument types,
261 261 you cannot use v8+.
262 262
263 263 Side effects within the VM, including modification of the arguments, are
264 264 not supported. If you need them, you cannot use v8+.
265 265
266 266 While the standard libnvpair functions may be used to inspect the arguments
267 267 to a method or function, v8+ also provides the `v8plus_args()` and
268 268 `v8plus_typeof()` convenience functions, which simplify checking the types
269 269 and obtaining the values of arguments.
270 270
271 271 ### int v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
272 272
273 273 This function checks `lp` for the exact sequence of arguments specified by
274 274 the list of types provided in the parameter list. If `V8PLUS_ARG_F_NOEXTRA`
275 275 is set in `flags`, the list of arguments must match exactly, with no
276 276 additional arguments. The parameter list must be terminated by
277 277 `V8PLUS_TYPE_NONE`.
278 278
279 279 Following `flags` is a list of argument data types and, for most data types,
280 280 pointers to locations at which the native C value of that argument should be
281 281 stored. The following JavaScript argument data types are supported; for
282 282 each, the parameter immediately following the data type parameter must be of
283 283 the indicated C type. This parameter may be `NULL`, in which case the value
284 284 will not be stored anywhere.
285 285
286 286 - V8PLUS_TYPE_NONE: used to terminate the parameter list only
287 287 - V8PLUS_TYPE_STRING: char **
288 288 - V8PLUS_TYPE_NUMBER: double *
289 289 - V8PLUS_TYPE_BOOLEAN: boolean_t *
290 290 - V8PLUS_TYPE_JSFUNC: v8plus_jsfunc_t *
291 291 - V8PLUS_TYPE_OBJECT: nvlist_t **
292 292 - V8PLUS_TYPE_NULL: no parameter
293 293 - V8PLUS_TYPE_UNDEFINED: no parameter
294 294 - V8PLUS_TYPE_INVALID: data_type_t (see below)
295 295 - V8PLUS_TYPE_ANY: nvpair_t **
296 296 - V8PLUS_TYPE_STRNUMBER64: uint64_t *
297 297 - V8PLUS_TYPE_INL_OBJECT: illegal
298 298
299 299 In most cases, the behaviour is straightforward: the value pointer parameter
300 300 provides a location into which the C value of the specified argument should
301 301 be stored. If the entire argument list matches the template, each
302 302 argument's C value is stored in its respective location. If not, no values
303 303 are stored, in the return value locations, `_v8plus_errno` is set
304 304 appropriately, and -1 is returned.
305 305
306 306 Three data types warrant further explanation: an argument of type
307 307 `V8PLUS_TYPE_INVALID` is any argument that may or may not match one of the
308 308 acceptable types. Its nvpair data type tag is stored and the argument
309 309 treated as matching. The value is ignored. `V8PLUS_TYPE_STRNUMBER64` is
310 310 used with strings that should be interpreted as 64-bit unsigned integers.
311 311 If the argument is not a string, or is not parseable as a 64-bit unsigned
312 312 integer, the argument will be treated as a mismatch. Finally,
313 313 `V8PLUS_TYPE_INL_OBJECT` is not supported with `v8plus_args()`; JavaScript
314 314 objects in the argument list must be individually inspected as nvlists.
315 315
316 316 A simple example:
317 317
318 318 double_t d;
319 319 boolean_t b;
320 320 char *s;
321 321 v8plus_jsfunc_t f;
322 322
323 323 /*
324 324 * This function requires exactly four arguments: a number, a
325 325 * boolean, a string, and a callback function. It is not acceptable
326 326 * to pass superfluous arguments to it.
327 327 */
328 328 if (v8plus_args(ap, V8PLUS_ARG_F_NOEXTRA,
329 329 V8PLUS_TYPE_NUMBER, &d,
330 330 V8PLUS_TYPE_BOOLEAN, &b,
331 331 V8PLUS_TYPE_STRING, &s,
332 332 V8PLUS_TYPE_JSFUNC, &f,
333 333 V8PLUS_TYPE_NONE) != 0)
334 334 return (NULL);
335 335
336 336 ### v8plus_type_t v8plus_typeof(const nvpair_t *pp)
337 337
338 338 This function simply returns the v8+ data type corresponding to the
339 339 name/value pair `pp`. If the value's type does not match the v8+ encoding
340 340 rules, `V8PLUS_TYPE_INVALID` is returned. This function cannot fail and
341 341 does not modify `_v8plus_errno`.
342 342
343 343 ### Returning Values
344 344
345 345 Similarly, when returning data across the boundary from C to C++, a
346 346 pointer to an nvlist must be returned. This object will be decoded in
347 347 the same manner as described above and returned to the JavaScript caller
348 348 of your method. Note that booleans, strings, and numbers will be encoded
349 349 as their primitive types, not objects. If you need to return something
350 350 containing these object types, you cannot use v8+. Other data types
351 351 cannot be represented. If you need to return them, you cannot use v8+.
352 352
353 353 The nvlist being returned must have one of two members: "res", an nvpair
354 354 containing the result of the call to be returned, or "err", an nvlist
355 355 containing members to be added to an exception. You may return a value of
356 356 any decodable type, and likewise may decorate an exception with properties
357 357 of any decodable type.
358 358
359 359 For convenience, you may return v8plus_void() instead of an nvlist,
360 360 which indicates successful execution of a function that returns nothing.
361 361
362 362 In addition, the `v8plus_obj()` routine is available for instantiating
363 363 JavaScript objects to return.
364 364
365 365 ### nvlist_t *v8plus_void(void)
366 366
367 367 This function clears `_v8plus_errno` and returns NULL. This is used to
368 368 indicate to internal v8+ code that the method or function should not return
369 369 a value.
370 370
371 371 ### nvlist_t *v8plus_obj(v8plus_type_t t, ...)
372 372
373 373 This function creates and populates an nvlist conforming to the encoding
374 374 rules of v8+ for returning a value or creating an exception. It can be used
375 375 to create anything from a single encoded value to arbitrarily nested
376 376 objects. It is essentially the inverse of `v8plus_args()` above, with a few
377 377 differences:
378 378
379 379 - It cannot be used to encode invalid or illegal data types.
380 380 - It accepts native C values, not pointers to them.
381 381 - Each value must be named.
382 382 - It can be used to encode nested objects inline using
383 383 `V8PLUS_TYPE_INL_OBJECT`, followed by type, name, value triples,
384 384 terminated with `V8PLUS_TYPE_NONE`.
385 385
386 386 This function can fail due to out-of-memory conditions, invalid or
387 387 unsupported data types, or, most commonly, programmer error in casting the
388 388 arguments to the correct type. *It is extremely important that data values,
389 389 particularly integers, be cast to the appropriate type (double) when passed
390 390 into this function!*
391 391
392 392 Following is a list of types and the C data types corresponding to their
393 393 values:
394 394
395 395 - V8PLUS_TYPE_NONE: used to terminate the parameter list only
396 396 - V8PLUS_TYPE_STRING: char *
397 397 - V8PLUS_TYPE_NUMBER: double
398 398 - V8PLUS_TYPE_BOOLEAN: boolean_t
399 399 - V8PLUS_TYPE_JSFUNC: v8plus_jsfunc_t
400 400 - V8PLUS_TYPE_OBJECT: nvlist_t *
401 401 - V8PLUS_TYPE_NULL: no parameter
402 402 - V8PLUS_TYPE_UNDEFINED: no parameter
403 403 - V8PLUS_TYPE_ANY: nvpair_t *
404 404 - V8PLUS_TYPE_STRNUMBER64: uint64_t
405 405 - V8PLUS_TYPE_INL_OBJECT: NONE-terminated type/value list
406 406
407 407 A simple example, in which we return a JavaScript object with two members,
408 408 one number and one embedded object with a 64-bit integer property. Note
409 409 that if this function fails, we will return `NULL` with `_v8plus_errno` set
410 410 appropriately, so v8+ will generate and throw an appropriate exception.
411 411
412 412 int x;
413 413 const char *s;
414 414
415 415 ...
416 416 return (v8plus_obj(
417 417 V8PLUS_TYPE_INL_OBJECT, "res",
418 418 V8PLUS_TYPE_NUMBER, "value", (double)x,
419 419 V8PLUS_TYPE_INL_OBJECT, "detail",
420 420 V8PLUS_TYPE_STRNUMBER64, "value64", s,
421 421 V8PLUS_TYPE_NONE,
422 422 V8PLUS_TYPE_NONE,
423 423 V8PLUS_TYPE_NONE));
424 424
425 425 The JSON representation of this object would be:
426 426
427 427 {
428 428 "res": {
429 429 "value": <x>,
430 430 "detail": {
431 431 "value64": "<s>"
432 432 }
433 433 }
434 434 }
435 435
436 436 ### v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
437 437
438 438 You can also add or replace the values of properties in an existing nvlist,
439 439 whether created using `nvlist_alloc()` directly or via `v8plus_obj()`. The
440 440 effect is very similar to `nvlist_merge()`, where the second list is created
441 441 on the fly from your argument list. The interpretation of the argument list
442 442 is the same as for `v8plus_obj()`, and the two functions are implemented
443 443 using the same logic.
444 444
445 445 ### Exceptions
446 446
447 447 If you are unable to create an nvlist to hold exception data, or you want a
448 448 generic exception to be thrown, return the value returned by v8plus_error().
449 449 In this case, the error code will be translated to an exception type and the
450 450 message string will be used as the message member of the exception. Other
451 451 members will not be present in the exception unless you also return an
452 452 nvlist containing an 'err' member (or, from a constructor, any nvlist) Only
453 453 basic v8-provided exception types can be thrown; if your addon needs to
454 454 throw some other kind of exception, you will need to either use v8 directly
455 455 or catch and re-throw from a JavaScript wrapper.
456 456
457 457 ## Errors
458 458
459 459 The v8plus_errno_t enumerated type and a family of utility functions are
460 460 automatically generated by generrno.js from a simple JSON file. The schema
461 461 of this file is as follows:
462 462
463 463 {
464 464 "error_base": <string>,
465 465 "errors": [
466 466 {
467 467 "code": <string>,
468 468 "msg": <string>,
469 469 "exception": <string>
470 470 },
471 471 ... ]
472 472 }
473 473
474 474 For each entry in the errors array, an identifier V8PLUSERR_code will be
475 475 added to v8plus_errno_t. By convention, code should be all upper case. The
476 476 default error message (present in JavaScript exceptions if a more specific
477 477 error message is not provided to v8plus_error()) is given by the msg
478 478 property. The exception property must be one of "Error", "TypeError",
479 479 "ReferenceError", "RangeError", or "SyntaxError"; i.e., the standard
480 480 exception types available in v8. This is the type of exception that will be
481 481 generated and thrown when a C function returns NULL with this error code
482 482 set. In addition, the built-in error codes V8PLUSERR_NOMEM,
483 483 V8PLUSERR_YOUSUCK, and V8PLUSERR_UNKNOWN are available for your use,
484 484 indicating an out of memory condition, programmer error (e.g., failure of
485 485 something you would assert in JavaScript), and an error code that cannot be
486 486 translated, respectively.
487 487
488 488 Set the make variable ERRNO_JSON to the name of this file.
489 489
490 490 To set the value of `_v8plus_errno`, use one of the following functions. It
491 491 is a bug to manipulate this variable directly.
492 492
493 493 ### nvlist_t *v8plus_verror(v8plus_errno_t e, const char *fmt, va_list)
494 494
495 495 This is the varargs analogue to `v8plus_error()` and has identical
496 496 semantics. Use this if you want to wrap manipulation of the v8+ error state
497 497 within your own varargs functions.
498 498
499 499 ### nvlist_t *v8plus_error(v8plus_errno_t e, const char *fmt, ...)
500 500
501 501 This function sets the value of `_v8plus_errno` to `e` and sets the
502 502 associated error message string to the formatted string `fmt` using the
503 503 argument list that follows. The format string and arguments are interpreted
504 504 as by `vsnprintf(3c)`. NULL is returned, suitable for returning directly
505 505 from a C function that provides a method if no exception decoration is
506 506 required.
507 507
508 508 If `fmt` is NULL, a generic default message is used; for consumer-defined
509 509 error codes, that message is the one provided in `errno.json`.
510 510
511 511 ### nvlist_t *v8plus_nverr(int errno, const char *propname)
512 512
513 513 This function sets the value of `_v8plus_errno` to a value mapped from the
514 514 system error code `errno` and sets the error message to a non-localised
515 515 explanation of the problem. The string `propname`, if non-NULL, is
516 516 indicated in the message as the name of the nvlist property being
517 517 manipulated when the error occurred. NULL is returned.
518 518
519 519 ### nvlist_t *v8plus_syserr(int errno, const char *fmt, ...)
520 520
521 521 Analogous to `v8plus_error()`, this function instead sets the error code to
522 522 a mapped value derived from the system error code `errno`. Not all error
523 523 codes can be mapped; those that are not known are mapped onto
524 524 `V8PLUSERR_UNKNOWN`. This function's semantics are otherwise identical to
525 525 those of `v8plus_error()`.
526 526
527 527 ### void v8plus_panic(const char *fmt, ...) __NORETURN
528 528
529 529 This function indicates a fatal runtime error. The format string `fmt` and
530 530 subsequent arguments are interpreted as by `vsnprintf(3c)` and written to
531 531 standard error, which is then flushed. `abort(3c)` or similar is then
532 532 invoked to terminate the Node.js process in which the addon is running. Use
533 533 of this function should be limited to those circumstances in which an
534 534 internal inconsistency has been detected that renders further progress
535 535 hazardous to user data or impossible.
536 536
537 537 ### Asynchrony
538 538
539 539 There are two main types of asynchrony supported by v8+. The first is the
540 540 deferred work model (using `uv_queue_work()` or the deprecated
541 541 `eio_custom()` mechanisms) frequently written about and demonstrated by
542 542 various practitioners around the world. In this model, your method or
543 543 function takes a callback argument and returns immediately after enqueuing a
544 544 task to run on one of the threads in the Node.js worker thread pool. That
545 545 task consists of a C function to be run on the worker thread, which may not
546 546 use any V8 (or v8+) state, and a function to be run in the main event loop
547 547 thread when that task has completed. The latter function is normally
548 548 expected to invoke the caller's original callback. In v8+, this takes the
549 549 following form:
550 550
551 551 void *
552 552 async_worker(void *cop, void *ctxp)
553 553 {
554 554 my_object_t *op = cop;
555 555 my_context_t *cp = ctxp;
556 556 my_result_t *rp = ...;
557 557
558 558 /*
559 559 * In thread pool context -- do not call any of the
560 560 * following functions:
561 561 * v8plus_obj_hold()
562 562 * v8plus_obj_rele()
563 563 * v8plus_jsfunc_hold()
564 564 * v8plus_jsfunc_rele()
565 565 * v8plus_call()
566 566 * v8plus_method_call()
567 567 * v8plus_defer()
568 568 *
569 569 * If you touch anything inside op, you may need locking to
570 570 * protect against functions called in the main thread.
571 571 */
572 572 ...
573 573
574 574 return (rp);
575 575 }
576 576
577 577 void
578 578 async_completion(void *cop, void *ctxp, void *resp)
579 579 {
580 580 my_object_t *op = cop;
581 581 my_context_t *cp = ctxp;
582 582 my_result_t *rp = resp;
583 583 nvlist_t *cbap;
584 584 nvlist_t *cbrp;
585 585
586 586 ...
587 587 cbap = v8plus_obj(
588 588 V8PLUS_TYPE_WHATEVER, "0", rp->mr_value,
589 589 V8PLUS_TYPE_NONE);
590 590
591 591 if (cbap != NULL) {
592 592 cbrp = v8plus_call(cp->mc_callback, cbap);
593 593 nvlist_free(cbap);
594 594 nvlist_free(cbrp);
595 595 }
596 596
597 597 v8plus_jsfunc_rele(cp->mc_callback);
598 598 free(cp);
599 599 free(rp);
600 600 }
601 601
602 602 nvlist_t *
603 603 async(void *cop, const nvlist_t *ap)
604 604 {
605 605 my_object_t *op = cop;
606 606 v8plus_jsfunc_t cb;
607 607 my_context_t *cp = malloc(sizeof (my_context_t));
608 608 ...
609 609 if (v8plus_args(ap, 0, V8PLUS_TYPE_JSFUNC, &cb,
610 610 V8PLUS_TYPE_NONE) != 0) {
611 611 free(cp);
612 612 return (NULL);
613 613 }
614 614
615 615 v8plus_jsfunc_hold(cb);
616 616 cp->mc_callback = cb;
↓ open down ↓ |
383 lines elided |
↑ open up ↑ |
617 617 v8plus_defer(op, cp, async_worker, async_completion);
618 618
619 619 return (v8plus_void());
620 620 }
621 621
622 622 This mechanism uses `uv_queue_work()` and as such will tie up one of the
623 623 worker threads in the pool for as long as `async_worker` is running.
624 624
625 625 The other asynchronous mechanism is the Node.js `EventEmitter` model. This
626 626 model requires some assistance from JavaScript code, because v8+ native
627 -objects no not inherit from `EventEmitter`. To make this work, you will
627 +objects do not inherit from `EventEmitter`. To make this work, you will
628 628 need to create a JavaScript object (the object your consumers actually use)
629 629 that inherits from `EventEmitter`, hang your native object off this object,
630 630 and populate the native object with an appropriate method that will cause
631 631 the JavaScript object to emit events when the native object invokes that
632 632 method. A simple example might look like this:
633 633
634 634 var util = require('util');
635 635 var binding = require('./native_binding');
636 636 var events = require('events');
637 637
638 638 function
639 639 MyObjectWrapper()
640 640 {
641 641 var self = this;
642 642
643 643 events.EventEmitter.call(this);
644 644 this._native = binding._create.apply(this,
645 645 Array.prototype.slice.call(arguments));
646 646 this._native._emit = function () {
647 647 var args = Array.prototype.slice.call(arguments);
648 648 self.emit.apply(self, args);
649 649 };
650 650 }
651 651 util.inherits(MyObjectWrapper, events.EventEmitter);
652 652
653 653 Then, in C code, you must arrange for libuv to call a C function in the
654 654 context of the main event loop. How to do this depends on what type of
655 655 asynchronous event you are waiting for; for example, the `uv_poll` mechanism
656 656 will call you back in the appropriate context when a file descriptor becomes
657 657 readable or writable. Because libuv is a C library, you can easily use
658 658 these interfaces directly in your v8+ addon. Your libuv callback might then
659 659 contain code looking something like this:
660 660
661 661 nvlist_t *eap;
662 662 nvlist_t *erp;
663 663 my_object_t *op = ...;
664 664 ...
665 665 eap = v8plus_obj(
666 666 V8PLUS_TYPE_STRING, "0", "my_event",
667 667 ...,
668 668 V8PLUS_TYPE_NONE);
669 669
670 670 if (eap != NULL) {
671 671 erp = v8plus_method_call(op, "_emit", eap);
672 672 nvlist_free(eap);
673 673 nvlist_free(erp);
674 674 }
675 675
676 676 This example will generate an event named "my_event" and propagate it to
677 677 listeners registered with the `MyObjectWrapper` instance. If additional
678 678 arguments are associated with the event, they may be added to `eap` and will
679 679 also be passed along to listeners as arguments to their callbacks.
680 680
681 681 ### void v8plus_obj_hold(const void *op)
682 682
683 683 Places a hold on the V8 representation of the specified C object. This is
684 684 rarely necessary; `v8plus_defer()` performs this action for you, but other
685 685 asynchronous mechanisms may require it. If you are returning from a method
686 686 call but have stashed a reference to the object somewhere and are not
687 687 calling `v8plus_defer()`, you must call this first. Holds and releases must
688 688 be balanced. Use of the object within a thread after releasing is a bug.
689 689
690 690 ### void v8plus_obj_rele(const void *op)
691 691
692 692 Releases a hold placed by `v8plus_obj_hold()`.
693 693
694 694 ### void v8plus_jsfunc_hold(v8plus_jsfunc_t f)
695 695
696 696 Places a hold on the V8 representation of the specified JavaScript function.
697 697 This is required when returning from a C function that has stashed a
698 698 reference to the function, typically to use it asynchronously as a callback.
699 699 All holds must be balanced with a release. Because a single hold is placed
700 700 on such objects when passed to you in an argument list (and released for you
701 701 when you return), it is legal to reference and even to invoke such a
702 702 function without first placing an additional hold on it.
703 703
704 704 ### void v8plus_jsfunc_rele(v8plus_jsfunc_t f)
705 705
706 706 Releases a hold placed by `v8plus_jsfunc_hold()`.
707 707
708 708 ### void v8plus_defer(void *op, void *ctx, worker, completion)
709 709
710 710 Enqueues work to be performed in the Node.js shared thread pool. The object
711 711 `op` and context `ctx` are passed as arguments to `worker` executing in a
712 712 thread from that pool. The same two arguments, along with the worker's
713 713 return value, are passed to `completion` executing in the main event loop
714 714 thread. See example above.
715 715
716 716 ### nvlist_t *v8plus_call(v8plus_jsfunc_t f, const nvlist_t *ap)
717 717
718 718 Calls the JavaScript function referred to by `f` with encoded arguments
719 719 `ap`. The return value is the encoded return value of the function. The
720 720 argument and return value encoding match the encodings that are used by C
721 721 functions that provide methods.
722 722
723 723 ### nvlist_t *v8plus_method_call(void *op, const char *name, const nvlist_t *ap)
724 724
725 725 Calls the method named by `name` in the native object `op` with encoded
726 726 argument list `ap`. The method must exist and must be a JavaScript
727 727 function. Such functions may be attached by JavaScript code as in the event
728 728 emitter example above. The effects of using this function to call a native
729 729 method are undefined.
730 730
731 731 ## FAQ
732 732
733 733 - Why?
734 734
735 735 Because C++ is garbage. Writing good software is challenging enough without
736 736 trying to understand a bunch of implicit side effects or typing templated
737 737 identifiers that can't fit in 80 columns without falling afoul of the
738 738 language's ambiguous grammar. Don't get me started.
739 739
740 740 - Why not use [FFI](https://github.com/rbranson/node-ffi)?
741 741
742 742 FFI is really cool; it offers us the ability to use C libraries without
743 743 writing bindings at all. However, it also exposes a lot of C nastiness to
744 744 JavaScript code, essentially placing the interface boundary in consuming
745 745 code itself. This pretty much breaks the JavaScript interface model --
746 746 for example, you can't really have a function that inspects the types of its
747 747 arguments -- and requires you to write an additional C library anyway if you
748 748 want or need to do something natively that's not quite what the C library
749 749 already does. Of course, one could use it to write "bindings" in JavaScript
750 750 that actually look like a JavaScript interface, which may end up being the
751 751 best answer, especially if those are autogenerated from CTF! In short, v8+
752 752 and FFI are different approaches to the problem. Use whichever fits your
753 753 need, and note that they're not mutually exclusive, either.
754 754
755 755 - What systems can I use this on?
756 756
757 757 [illumos](http://illumos.org) distributions, or possibly other platforms
758 758 with a working libnvpair. I'm sorry if your system doesn't have it; it's
759 759 open source and pretty easy to port.
760 760
761 761 There is an OSX port; see [the ZFS port's
762 762 implementation](http://code.google.com/p/maczfs/source/browse/#git%2Fusr%2Fsrc%2Flib%2Flibnvpair).
763 763 Unfortunately this port lacks the requisite support for floating-point data
764 764 (DATA_TYPE_DOUBLE) but you could easily add that from the illumos sources.
765 765
766 766 - What about node-waf and node-gyp?
767 767
768 768 Fuck python, fuck WAF, and fuck all the hipster douchebags for whom make is
769 769 too hard, too old, or "too Unixy". Make is simple, easy to use, and
770 770 extremely reliable. It was building big, important pieces of software when
771 771 your parents were young, and it Just Works. If you don't like using make
772 772 here, you probably don't want to use v8+ either, so just go away. Write
773 773 your CoffeeScript VM in something else, and gyp-scons-waf-rake your way to
774 774 an Instagram retirement in Bali with all your hipster douchebag friends.
775 775 Just don't bother me about it, because I don't care.
776 776
777 777 - Why is Node failing in dlopen()?
778 778
779 779 Most likely, your module has a typo or needs to be linked with a library.
780 780 Normally, shared objects like Node addons should be linked with -zdefs so that
781 781 these problems are found at build time, but Node doesn't deliver a mapfile
782 782 specifying its API so you're left with a bunch of undefined symbols you just
783 783 have to hope are defined somewhere in your node process's address space. If
784 784 they aren't, you're boned. LD_DEBUG=all will help you find the missing
785 785 symbol(s).
786 786
787 787 As of 0.0.2, v8+ builds a mapfile for your node binary at the time you build
788 788 your addon. It does not attempt to restrict the visibility of any symbols,
789 789 so you will not be warned if your addon is using private or deprecated
790 790 functionality in V8 or Node.js. Your build will, however, fail if you've
791 791 neglected to link in any required libraries, typo'd a symbol name, etc.
792 792
793 793 - Why use the old init() instead of NODE_MODULE()?
794 794
795 795 Because NODE_MODULE() is a macro that can't be passed another macro as the
796 796 name of your addon. Using it would therefore require the source to v8+ to
797 797 be generated at build time to match your module's name, which is
798 798 inconvenient. There may be a way to work around this.
799 799
800 800 - Why can't I see my exception's decorative properties in JavaScript?
801 801
802 802 Be careful when decorating exceptions. There are several built-in hidden
803 803 properties; if you decorate the exception with a property with the same
804 804 name, you will change the hidden property's value but it will still be
805 805 hidden. This almost certainly is not what you want, so you should prefix
806 806 the decorative property names with something unique to your module to avoid
807 807 stepping on V8's (or JavaScript's) property namespace.
808 808
809 809 - What if the factory model doesn't work for me?
810 810
811 811 See "License" below. Note also that one can export plain functions as well.
812 812
813 813 - Why do I always die with "invalid property type -3621" (or other garbage)?
814 814
815 815 You are passing an object with the wrong C type to `v8plus_obj()`. Like
816 816 all varargs functions, it cannot tell the correct size or type of the
817 817 objects you have passed it; they must match the preceding type argument or
818 818 it will not work correctly. In this particular case, you've most likely
819 819 done something like:
820 820
821 821 int foo = 0xdead;
822 822
823 823 v8plus_obj(V8PLUS_TYPE_NUMBER, "foo", foo, V8PLUS_TYPE_NONE);
824 824
825 825 An 'int' is 4 bytes in size, and the compiler reserves 4 bytes on the stack
826 826 and sticks the value of foo there. When `v8plus_obj` goes to read it, it
827 827 sees that the type is V8PLUS_TYPE_NUMBER, casts the address of the next
828 828 argument slot to a `double *`, and dereferences it, then moves the argument
829 829 list pointer ahead by the size of a double. Unfortunately, a double
830 830 is usually 8 bytes long, meaning that (a) the value of the property is going
831 831 to be comprised of the integer-encoded foo appended to the next data type,
832 832 and (b) the next data type is going to be read from either undefined memory
833 833 or from part of the address of the name of the next property. To cure this,
834 834 always make sure that you cast your integral arguments properly when using
835 835 V8PLUS_TYPE_NUMBER:
836 836
837 837 v8plus_obj(V8PLUS_TYPE_NUMBER, "foo", (double)foo, V8PLUS_TYPE_NONE);
838 838
839 839 ## License
840 840
841 841 MIT.
842 842
843 843 ## Bugs
844 844
845 845 See <https://github.com/wesolows/v8plus/issues>.
846 846
847 847 ## Consumers
848 848
849 849 This is an incomplete list of native addons known to be using v8+. If your
850 850 addon uses v8+, please let me know and I will include it here.
851 851
852 852 - <https://github.com/wesolows/node-contract>
↓ open down ↓ |
215 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX