--- /dev/null
+node-weak
+=========
+### Make weak references to JavaScript Objects.
+[![Build Status](https://secure.travis-ci.org/TooTallNate/node-weak.png)](http://travis-ci.org/TooTallNate/node-weak)
+
+On certain rarer occasions, you run into the need to be notified when a JavaScript
+object is going to be garbage collected. This feature is exposed to V8's C++ API,
+but not to JavaScript.
+
+That's where `node-weak` comes in! This module exports V8's `Persistent<Object>`
+functionality to JavaScript. This allows you to create weak references, and
+optionally attach a callback function to any arbitrary JS object. The callback
+function will be invoked right before the Object is garbage collected (i.e. after
+there are no more remaining references to the Object in JS-land).
+
+This module can, for example, be used for debugging; to determine whether or not
+an Object is being garbage collected as it should.
+Take a look at the example below for commented walkthrough scenario.
+
+
+Installation
+------------
+
+Install with `npm`:
+
+``` bash
+$ npm install weak
+```
+
+
+Example
+-------
+
+Here's an example of calling a `cleanup()` function on a Object before it gets
+garbage collected:
+
+``` js
+var weak = require('weak')
+
+// we are going to "monitor" this Object and invoke "cleanup"
+// before the object is garbage collected
+var obj = {
+ a: true
+ , foo: 'bar'
+}
+
+// The function to call before Garbage Collection.
+// Note that by the time this is called, 'obj' has been set to `null`.
+function cleanup (o) {
+ delete o.a
+ delete o.foo
+}
+
+// Here's where we set up the weak reference
+var ref = weak(obj, function () {
+ // `this` inside the callback is the 'obj'. DO NOT store any new references
+ // to the object, and DO NOT use the object in any async functions.
+ cleanup(this)
+})
+
+// While `obj` is alive, `ref` proxies everything to it, so:
+ref.a === obj.a
+ref.foo === obj.foo
+
+// Clear out any references to the object, so that it will be GC'd at some point...
+obj = null
+
+//
+//// Time passes, and the garbage collector is run
+//
+
+// `callback()` above is called, and `ref` now acts like an empty object.
+typeof ref.foo === 'undefined'
+```
+
+
+API
+---
+
+### weakref weak(Object obj [, Function callback])
+
+The main exports is the function that creates the weak reference.
+The first argument is the Object that should be monitored.
+The Object can be a regular Object, an Array, a Function, a RegExp, or any of
+the primitive types or constructor function created with `new`.
+Optionally, you can set a callback function to be invoked
+before the object is garbage collected.
+
+
+### Object weak.get(weakref ref)
+
+`get()` returns the actual reference to the Object that this weak reference was
+created with. If this is called with a dead reference, `undefined` is returned.
+
+
+### Boolean weak.isDead(weakref ref)
+
+Checks to see if `ref` is a dead reference. Returns `true` if the original Object
+has already been GC'd, `false` otherwise.
+
+
+### null weak.addCallback(weakref ref, Function callback)
+
+Adds `callback` to the Array of callback functions that will be invoked before the
+Objects gets garbage collected. The callbacks get executed in the order that they
+are added.
+
+
+### Array weak.callbacks(weakref ref)
+
+Returns the internal `Array` that `ref` iterates through to invoke the GC
+callbacks. The array can be `push()`ed or `unshift()`ed onto, to have more control
+over the execution order of the callbacks. This is similar in concept to node's
+`EventEmitter#listeners()` function.
--- /dev/null
+/*
+ * Copyright (c) 2011, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "v8.h"
+#include "node.h"
+
+using namespace v8;
+using namespace node;
+
+namespace {
+
+
+typedef struct proxy_container {
+ Persistent<Object> proxy;
+ Persistent<Object> target;
+ Persistent<Array> callbacks;
+} proxy_container;
+
+
+Persistent<ObjectTemplate> proxyClass;
+
+
+bool IsDead(Handle<Object> proxy) {
+ assert(proxy->InternalFieldCount() == 1);
+ proxy_container *cont = reinterpret_cast<proxy_container*>(
+ proxy->GetPointerFromInternalField(0));
+ return cont == NULL || cont->target.IsEmpty();
+}
+
+
+Handle<Object> Unwrap(Handle<Object> proxy) {
+ assert(!IsDead(proxy));
+ proxy_container *cont = reinterpret_cast<proxy_container*>(
+ proxy->GetPointerFromInternalField(0));
+ return cont->target;
+}
+
+Handle<Array> GetCallbacks(Handle<Object> proxy) {
+ proxy_container *cont = reinterpret_cast<proxy_container*>(
+ proxy->GetPointerFromInternalField(0));
+ assert(cont != NULL);
+ return cont->callbacks;
+}
+
+
+#define UNWRAP \
+ HandleScope scope; \
+ Handle<Object> obj; \
+ const bool dead = IsDead(info.This()); \
+ if (!dead) obj = Unwrap(info.This()); \
+
+
+Handle<Value> WeakNamedPropertyGetter(Local<String> property,
+ const AccessorInfo& info) {
+ UNWRAP
+ return dead ? Local<Value>() : obj->Get(property);
+}
+
+
+Handle<Value> WeakNamedPropertySetter(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ UNWRAP
+ if (!dead) obj->Set(property, value);
+ return value;
+}
+
+
+Handle<Integer> WeakNamedPropertyQuery(Local<String> property,
+ const AccessorInfo& info) {
+ return HandleScope().Close(Integer::New(None));
+}
+
+
+Handle<Boolean> WeakNamedPropertyDeleter(Local<String> property,
+ const AccessorInfo& info) {
+ UNWRAP
+ return Boolean::New(!dead && obj->Delete(property));
+}
+
+
+Handle<Value> WeakIndexedPropertyGetter(uint32_t index,
+ const AccessorInfo& info) {
+ UNWRAP
+ return dead ? Local<Value>() : obj->Get(index);
+}
+
+
+Handle<Value> WeakIndexedPropertySetter(uint32_t index,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ UNWRAP
+ if (!dead) obj->Set(index, value);
+ return value;
+}
+
+
+Handle<Integer> WeakIndexedPropertyQuery(uint32_t index,
+ const AccessorInfo& info) {
+ return HandleScope().Close(Integer::New(None));
+}
+
+
+Handle<Boolean> WeakIndexedPropertyDeleter(uint32_t index,
+ const AccessorInfo& info) {
+ UNWRAP
+ return Boolean::New(!dead && obj->Delete(index));
+}
+
+
+Handle<Array> WeakPropertyEnumerator(const AccessorInfo& info) {
+ UNWRAP
+ return HandleScope().Close(dead ? Array::New(0) : obj->GetPropertyNames());
+}
+
+
+void AddCallback(Handle<Object> proxy, Handle<Function> callback) {
+ Handle<Array> callbacks = GetCallbacks(proxy);
+ callbacks->Set(Integer::New(callbacks->Length()), callback);
+}
+
+
+void TargetCallback(Persistent<Value> target, void* arg) {
+ HandleScope scope;
+
+ assert(target.IsNearDeath());
+
+ proxy_container *cont = reinterpret_cast<proxy_container*>(arg);
+
+ // invoke any listening callbacks
+ uint32_t len = cont->callbacks->Length();
+ Handle<Value> argv[1];
+ argv[0] = target;
+ for (uint32_t i=0; i<len; i++) {
+
+ Handle<Function> cb = Handle<Function>::Cast(
+ cont->callbacks->Get(Integer::New(i)));
+
+ TryCatch try_catch;
+
+ cb->Call(target->ToObject(), 1, argv);
+
+ if (try_catch.HasCaught()) {
+ FatalException(try_catch);
+ }
+ }
+
+ cont->proxy->SetPointerInInternalField(0, NULL);
+ cont->proxy.Dispose();
+ cont->proxy.Clear();
+ cont->target.Dispose();
+ cont->target.Clear();
+ cont->callbacks.Dispose();
+ cont->callbacks.Clear();
+ free(cont);
+}
+
+
+Handle<Value> Create(const Arguments& args) {
+ HandleScope scope;
+
+ if (!args[0]->IsObject()) {
+ Local<String> message = String::New("Object expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+
+ proxy_container *cont = (proxy_container *)
+ malloc(sizeof(proxy_container));
+
+ cont->target = Persistent<Object>::New(args[0]->ToObject());
+ cont->callbacks = Persistent<Array>::New(Array::New());
+
+ cont->proxy = Persistent<Object>::New(proxyClass->NewInstance());
+ cont->proxy->SetPointerInInternalField(0, cont);
+
+ cont->target.MakeWeak(cont, TargetCallback);
+
+ if (args.Length() >= 2) {
+ AddCallback(cont->proxy, Handle<Function>::Cast(args[1]));
+ }
+
+ return cont->proxy;
+}
+
+/**
+ * TODO: Make this better.
+ */
+
+bool isWeakRef (Handle<Value> val) {
+ return val->IsObject() && val->ToObject()->InternalFieldCount() == 1;
+}
+
+Handle<Value> IsWeakRef (const Arguments& args) {
+ HandleScope scope;
+ return Boolean::New(isWeakRef(args[0]));
+}
+
+Handle<Value> Get(const Arguments& args) {
+ HandleScope scope;
+
+ if (!isWeakRef(args[0])) {
+ Local<String> message = String::New("Weakref instance expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+ Local<Object> proxy = args[0]->ToObject();
+
+ const bool dead = IsDead(proxy);
+ if (dead) return Undefined();
+
+ Handle<Object> obj = Unwrap(proxy);
+ return scope.Close(obj);
+}
+
+Handle<Value> IsNearDeath(const Arguments& args) {
+ HandleScope scope;
+
+ if (!isWeakRef(args[0])) {
+ Local<String> message = String::New("Weakref instance expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+ Local<Object> proxy = args[0]->ToObject();
+
+ proxy_container *cont = reinterpret_cast<proxy_container*>(
+ proxy->GetPointerFromInternalField(0));
+ assert(cont != NULL);
+
+ Handle<Boolean> rtn = Boolean::New(cont->target.IsNearDeath());
+
+ return scope.Close(rtn);
+}
+
+Handle<Value> IsDead(const Arguments& args) {
+ HandleScope scope;
+
+ if (!isWeakRef(args[0])) {
+ Local<String> message = String::New("Weakref instance expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+ Local<Object> proxy = args[0]->ToObject();
+
+ const bool dead = IsDead(proxy);
+ return Boolean::New(dead);
+}
+
+
+Handle<Value> AddCallback(const Arguments& args) {
+ HandleScope scope;
+
+ if (!isWeakRef(args[0])) {
+ Local<String> message = String::New("Weakref instance expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+ Local<Object> proxy = args[0]->ToObject();
+
+ AddCallback(proxy, Handle<Function>::Cast(args[1]));
+
+ return Undefined();
+}
+
+Handle<Value> Callbacks(const Arguments& args) {
+ HandleScope scope;
+
+ if (!isWeakRef(args[0])) {
+ Local<String> message = String::New("Weakref instance expected");
+ return ThrowException(Exception::TypeError(message));
+ }
+ Local<Object> proxy = args[0]->ToObject();
+
+ return scope.Close(GetCallbacks(proxy));
+}
+
+
+void Initialize(Handle<Object> target) {
+ HandleScope scope;
+
+ proxyClass = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ proxyClass->SetNamedPropertyHandler(WeakNamedPropertyGetter,
+ WeakNamedPropertySetter,
+ WeakNamedPropertyQuery,
+ WeakNamedPropertyDeleter,
+ WeakPropertyEnumerator);
+ proxyClass->SetIndexedPropertyHandler(WeakIndexedPropertyGetter,
+ WeakIndexedPropertySetter,
+ WeakIndexedPropertyQuery,
+ WeakIndexedPropertyDeleter,
+ WeakPropertyEnumerator);
+ proxyClass->SetInternalFieldCount(1);
+
+ NODE_SET_METHOD(target, "get", Get);
+ NODE_SET_METHOD(target, "create", Create);
+ NODE_SET_METHOD(target, "isWeakRef", IsWeakRef);
+ NODE_SET_METHOD(target, "isNearDeath", IsNearDeath);
+ NODE_SET_METHOD(target, "isDead", IsDead);
+ NODE_SET_METHOD(target, "callbacks", Callbacks);
+ NODE_SET_METHOD(target, "addCallback", AddCallback);
+
+}
+
+} // anonymous namespace
+
+NODE_MODULE(weakref, Initialize);