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