And you said you didn't care for JSAPI! :P
On Mon, Jul 8, 2013 at 11:58 PM, Tim Caswell <[email protected]> wrote: > After a quick chat with TJ and Trevor on IRC, I've decided to write up a > quick sample of the kind of API I would like as a JavaScript person. The > evolving code is at https://gist.github.com/creationix/5954513. > > I'll inline some snippets here for conversation. > > First is an ultra simple library that has an "addNumbers" function that > adds two values as integers. > > #include "js_api.h" > > > > // Equal to the following JS snippet: > // function addNumbers(a, b) { a |= 0; b |= 0; return a + b; } > > bool simple_add_numbers(js_context* C) { > > int32_t a = js_to_int32(C, 1); > > int32_t b = js_to_int32(C, 2); > > return js_return(C, js_create_int32(C, a + b)); > > } > > // The module exports an object with functions on it. > bool export_simple(js_context* C) { > > int exports = js_create_object(C); // Create a new object and place it on > the stack > > js_set_function(C, exports, "addNumbers", simple_add_numbers); > > return js_return(C, exports); > > } > > // If we wanted to export the add function directly, we could do > int export_simple(js_context* C) { > > return js_return_function(C, simple_add_numbers); > > } > > > > A slightly more complicated example is one that creates a constructor with > a prototype method. > > #include "js_api.h" > > // Sometimes you want to access the js "this" value in a function > > > // function Point(x, y) { this.x = x; this.y = y; } > bool point_constructor(js_context* C) { > > js_set(C, 0, "x", 1); // this.x = arguments[0] > > js_set(C, 0, "y", 2); // this.y = arguments[1] > > return true; // js returns undefined, but C returns true meaning > no error. > > } > > // Point.prototype.add = function () { var x = this.x | 0; var y = this.y | > 0; return x + y; }; > bool point_add(js_context* C) { > > int32_t x = js_get_int32(C, 0, "x"); // var x = this.x > > int32_t y = js_get_int32(C, 0, "y"); // var y = this.y > > return js_return_int32(C, x + y); // return x + y > > } > > bool export_point(js_context* C) { > > int Point = js_create_function(C, point_constructor); > > // All functions in JavaScript already have a prototype object. Get it. > > int proto = js_load(C, Point, "prototype"); > > // Add a function to the prototype object > js_set_function(C, proto, "add", point_add); > > // Then return the constructor as the module exports > > return js_return(C, Point); > > } > > I'm still working out sample APIs for things like passing a C struct to > JS and back, wrapping external binary buffers, and binary data in general, > but this should be enough to get a general idea of the kind of API I'm > looking for. > > The important thing is that it's as 1:1 with the JS it represents as > possible, but in simple C. > > > On Mon, Jul 8, 2013 at 9:58 PM, Tim Caswell <[email protected]> wrote: > >> I can vouch for a stable and documented C API. The main thing that >> stopped me from re-implementing node on top of SpiderMonkey was the SM APIs >> changing constantly and the docs on the wiki being constantly out of date. >> They were too new for the last stable release and too old for the latest >> mainline code. (Also the build system for SM itself was a mess and very >> particular). >> >> Contrast with my luvit project which I did largely on my own for the >> first few months and had a good chunk of node re-implemented. Lua's C API >> is simple and consistent. I didn't have to use C++ at all which made >> things much easier for me. C is a fairly simple language, C++ is crazy >> complicated. >> >> I'm a scripter. I've been writing programs in scripting languages for >> over 20 years. I've been know to write a node addon or two because I >> really like the extra power it gives (node-webgl was a fun 24 hour hack). >> >> My only advice for the new common API is to keep it simple and avoid C++ >> classes. I'm not encouraging the stack oriented nature of the Lua API, but >> being able to work in terms of objects, properties, functions, etc (the >> same terms you use in JS) would be great. Treat everything as the simple >> data it is. >> >> I can draft some APIs based on my experience with writing libuv bindings >> for various runtimes if you're interested. >> >> -Tim Caswell >> >> >> On Mon, Jul 8, 2013 at 1:35 PM, Timothy J Fontaine >> <[email protected]>wrote: >> >>> [cross post from http://atxconsulting.com/2013/07/06/rewrite-it-anyway/] >>> >>> Node v1.0 is approaching, and v0.12 is imminent (as far as that goes for >>> FOSS >>> projects). As we work towards getting v0.12 out the door, there have >>> been a lot >>> of changes happening for node's primary dependency v8. Ben is working on >>> moving >>> us to the 3.20 branch, follow his progress >>> [here](https://github.com/joyent/node/pull/5804). >>> >>> As you can tell this is a signficant change to the API, which requires a >>> touch >>> of virtually every file in our `src/`, this has been a huge headache >>> for him, >>> and will ultimately cause a huge headache for developers of binary >>> addons. >>> >>> You're going to have to `#ifdef` around significant portions of the API >>> to keep >>> your module working across different version of node, this is going to >>> cause >>> endless amounts of pain and issues for node and developers who have for >>> the >>> most part been accepting of the churn in our underspecified addon API. >>> >>> This one is going to hurt. >>> >>> A lot. >>> >>> ## TL;DR -- A modest proposal >>> >>> Since you're going to have to rewrite your module anyway, it's time for >>> node to >>> specify and export the API we are going to "bless" for addons. That is, >>> just >>> what API we are going to support and make sure continues to work from >>> minor and >>> major releases, as well as a deprecation policy. >>> >>> More specifically I think we should be exporting a separate (and not >>> equal) >>> wrapper around (at the very least) javascript object creation, get/set, >>> function >>> calling. >>> >>> Additionally we should package and distribute (if possible in npm) a >>> transitional library/headers which module authors can target today which >>> will >>> allow their module to compile and work from v0.8 through v1.0 >>> >>> ## The Platform Problem >>> >>> We currently allow platforms/distributors to build against shared (their >>> own) >>> versions of many of our dependencies, including but not limited to: >>> >>> * v8 >>> - Holy crap, we're about as tightly coupled to the version of v8 we >>> ship as >>> chromium itself is. >>> * libuv >>> - If we weren't strictly coupled to v8, we certainly are for libuv, >>> there >>> would be no (useful) node, without libuv. >>> * openssl >>> - This is a must for linux distributions, who like to break DSA keys >>> and then >>> make every dependency vulnerable as a result (sorry Debian, I keed I >>> keed). >>> - This actually allows distributors who know specific things about >>> their >>> platform to enable/disable the features that allow it to run best. >>> * zlib >>> - Meh, this isn't such a big deal, it doesn't really change all that >>> often. >>> * http_parser >>> - Really? People ship this as a separate library? >>> >>> This functionality was added to appease platform builders, the likes of >>> Debian, >>> Fedora, and even SmartOS. However, doing so has complicated and muddled >>> the >>> scenario of building and linking binary addons. >>> >>> Currently node-gyp downloads the sourceball, extracts the headers from >>> it, >>> and makes some assumptions from `process.config` about how to build your >>> addon. >>> In practice this has been working reasonably well. >>> >>> However, I'm very concerned about this as a long term strategy. It's >>> possible >>> for someone to have tweaked or twisted the node (or one of its >>> dependencies) >>> builds, which could lead to some unintended consequences. In the "best" >>> case, >>> you'll get a compiler error from a changed API or clashing symbol. In >>> the worst >>> case they have modified the ABI which will manifest itself in unexpected >>> and >>> often subtle ways. >>> >>> Not to mention that we have no good answer on how to build and link addon >>> modules against the proper version of a shared dependency (what if the >>> system >>> has multiple openssl's, what if they compiled against it in one place, >>> but now >>> run against it in another). >>> >>> And last but not least, how do modules consume symbols from our >>> dependencies >>> that node itself doesn't consume. Consider a specific crypto routine from >>> openssl that you want to provide as an addon module because node doesn't >>> currently have an interface for it. >>> >>> ## Enemies without, and enemies within >>> >>> As if it weren't bad enough that platforms may ship against a version of >>> v8 >>> that we haven't blessed, we (and addon developers) have to fight against >>> the >>> beast that is the v8 API churn. >>> >>> I don't really fault Google and the chromium or v8 team for how they are >>> handling this, more often then not we just end up with ugly compile time >>> deprecation warnings, letting us know the world is about to break. >>> >>> However, there have been times -- like right now -- where node can't >>> paper over >>> the drastic change in the v8 API for module developers. And as a result >>> we >>> begrudgingly pass the API change to module authors. >>> >>> To paraphrase, don't forget that execrement will inevitably lose its >>> battle >>> with gravity. >>> >>> So what are we going to do? >>> >>> ## Meat and Potatoes >>> >>> This is where I don't particularly have everything fleshed out, and I'm >>> sure I >>> will take a considerable amount of heat from people on API decisions that >>> haven't been made. >>> >>> I want to export the following interfaces: >>> >>> * `node/js.h` >>> - Object creation and manipulation. >>> - Function calling and Error throwing. >>> * `node/platform.h` >>> - IO and event loop abstraction. >>> * `node/ssl.h` >>> * `node/zlib.h` >>> * `node/http.h` >>> >>> While I am not particularly attached to the names of these headers, each >>> represent an interface that I think module authors would opt to target. >>> I only >>> feel strongly that we export `js` and `platform` as soon as possible as >>> they are the primary interactions for every module. >>> >>> ### Basic Principles >>> >>> There are only a few principles: >>> >>> * Avoid (like the plague) any scenario where we expose an ABI to module >>> authors. >>> - Where possible use opaque handles and getter/setter functions. >>> * The exported API should be a reliable interface which authors can >>> depend on >>> working across releases. >>> * While a dependency may change its API, we have committed to our >>> external API >>> and need to provide a transitional interface in accordance with our >>> deprecation >>> policy. >>> * The API should never expose an implementation detail to module >>> authors (A >>> spidermonkey backed node one day?). >>> >>> ### Platform >>> >>> The `platform` interface is the easiest to discuss, but the pattern would >>> follow for `ssl`, `zlib`, and `http`. >>> >>> This would just rexport the existing `uv` API, however with a C-style >>> namespace >>> of `node_`. Any struct passing should be avoided, and libuv would need >>> to be >>> updated to reflect that. >>> >>> ### JS >>> >>> I expect the `js` interface to be the most contentious, and also fraught >>> with >>> peril. >>> >>> The interface for addon authors should be C, I don't want to forsake the >>> C++ >>> folk, but I think the binding for that should be based on our C >>> interface. >>> >>> I was going to describe my ideal interface, and frame it in context of >>> my ruby >>> and python experience. However, after a brief investigation, the JSAPI >>> for >>> spidermonkey exports almost exactly the API I had in mind. So read about >>> that >>> [here]( >>> https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_User_Guide). >>> >>> Would it make sense, and would it be worth the effort, for node to >>> export a >>> JSAPI compatible interface? >>> >>> Would it make more sense to export a JSAPI influenced API currently >>> targetted >>> at v8 which could be trivially extended to also support spidermonkey? >>> >>> UPDATE 2013-07-08: >>> >>> > It's interesting and worthy to have a conversation about being able to >>> > provide a backend neutral object model, though our current coupling to >>> v8 and >>> > its usage in existing addons may not make it possible to entirely hide >>> away >>> > the eccentricities of the v8 API. But what we can provide is an >>> interface >>> > that is viable to target against from release to release regardless of >>> how >>> > the public v8 API changes. >>> >>> ## Prior Art >>> >>> A lot of these ideas came from a discussion I had with >>> [Joshua Clulow](http://blog.sysmgr.org/) while en route to >>> [NodeConf](http://nodeconf.com). >>> >>> Part of that conversation was about [v8+]( >>> https://github.com/wesolows/v8plus) >>> which was written by a particularly talented coworker, who had a rather >>> nasty >>> experience writing for the existing C++ API (such as it is). >>> >>> There's some overlap in how it works and how I envisioned the new API. >>> However, >>> I'm not sure I'm particularly fond of automatically converting objects >>> into >>> nvlists, though that does solve some of the release and retain issues. >>> >>> In general I would advocate opaque handles and getter and setter >>> functions, >>> with a helper API which could do that wholesale conversion for you. >>> >>> Really though this matters less in a world where addon authors are >>> following >>> some defined "Best Practices". >>> >>> * Only pass and return "primitives" to/from the javascript/C boundary >>> - Primitives would be things like: `String`, `Number`, `Buffer`. >>> * Only perform objection manipulation in javascript where the JIT can >>> work >>> its magic >>> >>> ## Dessert >>> >>> Work on this needs to begin as soon as possible. We should be able to >>> distribute it in npm, and authors should be able to target it by >>> including a >>> few headers in their source and adding a dependency stanza in their >>> `binding.gyp`, and by doing so their module will work from v0.8 through >>> v1.0 >>> >>> I mean, you're going to have to rewrite it anyway. >>> >>> -- >>> -- >>> Job Board: http://jobs.nodejs.org/ >>> Posting guidelines: >>> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines >>> You received this message because you are subscribed to the Google >>> Groups "nodejs" group. >>> To post to this group, send email to [email protected] >>> To unsubscribe from this group, send email to >>> [email protected] >>> For more options, visit this group at >>> http://groups.google.com/group/nodejs?hl=en?hl=en >>> >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "nodejs" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to [email protected]. >>> For more options, visit https://groups.google.com/groups/opt_out. >>> >>> >>> >> >> > -- > -- > Job Board: http://jobs.nodejs.org/ > Posting guidelines: > https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines > You received this message because you are subscribed to the Google > Groups "nodejs" group. > To post to this group, send email to [email protected] > To unsubscribe from this group, send email to > [email protected] > For more options, visit this group at > http://groups.google.com/group/nodejs?hl=en?hl=en > > --- > You received this message because you are subscribed to the Google Groups > "nodejs" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/groups/opt_out. > > > -- -- Job Board: http://jobs.nodejs.org/ Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines You received this message because you are subscribed to the Google Groups "nodejs" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/nodejs?hl=en?hl=en --- You received this message because you are subscribed to the Google Groups "nodejs" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/groups/opt_out.
