test: bundle node-weak in test/gc so that it doesn't need to be downloaded
authorNathan Rajlich <nathan@tootallnate.net>
Wed, 13 Jun 2012 22:55:29 +0000 (00:55 +0200)
committerBert Belder <bertbelder@gmail.com>
Wed, 13 Jun 2012 22:56:35 +0000 (00:56 +0200)
Makefile
test/gc/node_modules/weak/LICENSE [new file with mode: 0644]
test/gc/node_modules/weak/README.md [new file with mode: 0644]
test/gc/node_modules/weak/binding.gyp [new file with mode: 0644]
test/gc/node_modules/weak/lib/weak.js [new file with mode: 0644]
test/gc/node_modules/weak/package.json [new file with mode: 0644]
test/gc/node_modules/weak/src/weakref.cc [new file with mode: 0644]
vcbuild.bat

index f2b9c32..52d90f0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -61,17 +61,16 @@ test-http1: all
 test-valgrind: all
        $(PYTHON) tools/test.py --mode=release --valgrind simple message
 
-node_modules/weak:
+test/gc/node_modules/weak/build:
        @if [ ! -f node ]; then make all; fi
-       @if [ ! -d node_modules ]; then mkdir -p node_modules; fi
-       ./node deps/npm/bin/npm-cli.js install weak \
-               --nodedir="$(shell pwd)" \
-               --prefix="$(shell pwd)" --unsafe-perm # go ahead and run as root.
+       ./node deps/npm/node_modules/node-gyp/bin/node-gyp rebuild \
+               --directory="$(shell pwd)/test/gc/node_modules/weak" \
+               --nodedir="$(shell pwd)"
 
-test-gc: all node_modules/weak
+test-gc: all test/gc/node_modules/weak/build
        $(PYTHON) tools/test.py --mode=release gc
 
-test-all: all node_modules/weak
+test-all: all test/gc/node_modules/weak/build
        $(PYTHON) tools/test.py --mode=debug,release
        make test-npm
 
diff --git a/test/gc/node_modules/weak/LICENSE b/test/gc/node_modules/weak/LICENSE
new file mode 100644 (file)
index 0000000..834ec92
--- /dev/null
@@ -0,0 +1,13 @@
+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.
\ No newline at end of file
diff --git a/test/gc/node_modules/weak/README.md b/test/gc/node_modules/weak/README.md
new file mode 100644 (file)
index 0000000..9e8a916
--- /dev/null
@@ -0,0 +1,114 @@
+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.
diff --git a/test/gc/node_modules/weak/binding.gyp b/test/gc/node_modules/weak/binding.gyp
new file mode 100644 (file)
index 0000000..68e9eb1
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  'targets': [
+    {
+      'target_name': 'weakref',
+      'sources': [ 'src/weakref.cc' ]
+    }
+  ]
+}
diff --git a/test/gc/node_modules/weak/lib/weak.js b/test/gc/node_modules/weak/lib/weak.js
new file mode 100644 (file)
index 0000000..b32ac46
--- /dev/null
@@ -0,0 +1,9 @@
+var bindings = require('../build/Release/weakref.node')
+module.exports = bindings.create
+
+// backwards-compat with node-weakref
+bindings.weaken = bindings.create
+
+Object.keys(bindings).forEach(function (name) {
+  module.exports[name] = bindings[name]
+})
diff --git a/test/gc/node_modules/weak/package.json b/test/gc/node_modules/weak/package.json
new file mode 100644 (file)
index 0000000..f2e952b
--- /dev/null
@@ -0,0 +1,55 @@
+{
+  "author": {
+    "name": "Ben Noordhuis",
+    "email": "info@bnoordhuis.nl"
+  },
+  "contributors": [
+    {
+      "name": "Nathan Rajlich",
+      "email": "nathan@tootallnate.net",
+      "url": "http://tootallnate.net"
+    }
+  ],
+  "name": "weak",
+  "description": "Make weak references to JavaScript Objects.",
+  "keywords": [
+    "weak",
+    "reference",
+    "js",
+    "javascript",
+    "object",
+    "function",
+    "callback"
+  ],
+  "version": "0.2.1",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/TooTallNate/node-weak.git"
+  },
+  "main": "./lib/weak.js",
+  "scripts": {
+    "test": "mocha -gc --reporter spec",
+    "install": "node-gyp rebuild"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "dependencies": {
+    "bindings": "*"
+  },
+  "devDependencies": {
+    "mocha": "> 0.7.0",
+    "should": "*"
+  },
+  "_npmUser": {
+    "name": "tootallnate",
+    "email": "nathan@tootallnate.net"
+  },
+  "_id": "weak@0.2.1",
+  "optionalDependencies": {},
+  "_engineSupported": true,
+  "_npmVersion": "1.1.25",
+  "_nodeVersion": "v0.7.10",
+  "_defaultsLoaded": true,
+  "_from": "weak"
+}
diff --git a/test/gc/node_modules/weak/src/weakref.cc b/test/gc/node_modules/weak/src/weakref.cc
new file mode 100644 (file)
index 0000000..409b215
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * 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);
index 20d4b03..8948d70 100644 (file)
@@ -28,6 +28,7 @@ set msi=
 set licensertf=
 set upload=
 set jslint=
+set buildnodeweak=
 set noetw=
 set noetw_arg=
 set noetw_msi_arg=
@@ -51,7 +52,8 @@ if /i "%1"=="test-internet" set test=test-internet&goto arg-ok
 if /i "%1"=="test-pummel"   set test=test-pummel&goto arg-ok
 if /i "%1"=="test-simple"   set test=test-simple&goto arg-ok
 if /i "%1"=="test-message"  set test=test-message&goto arg-ok
-if /i "%1"=="test-all"      set test=test-all&goto arg-ok
+if /i "%1"=="test-gc"       set test=test-gc&set buildnodeweak=1&goto arg-ok
+if /i "%1"=="test-all"      set test=test-all&set buildnodeweak=1&goto arg-ok
 if /i "%1"=="test"          set test=test&goto arg-ok
 if /i "%1"=="msi"           set msi=1&set licensertf=1&goto arg-ok
 if /i "%1"=="upload"        set upload=1&goto arg-ok
@@ -62,6 +64,7 @@ echo Warning: ignoring invalid command line option `%1`.
 :arg-ok
 shift
 goto next-arg
+
 :args-done
 if defined upload goto upload
 if defined jslint goto jslint
@@ -139,13 +142,24 @@ if "%test%"=="test-internet" set test_args=%test_args% internet
 if "%test%"=="test-pummel" set test_args=%test_args% pummel
 if "%test%"=="test-simple" set test_args=%test_args% simple
 if "%test%"=="test-message" set test_args=%test_args% message
+if "%test%"=="test-gc" set test_args=%test_args% gc
 if "%test%"=="test-all" set test_args=%test_args%
 
+:build-node-weak
+@rem Build node-weak if required
+if "%buildnodeweak%"=="" goto run-tests
+"%config%\node" deps\npm\node_modules\node-gyp\bin\node-gyp rebuild --directory="%~dp0test\gc\node_modules\weak" --nodedir="%~dp0."
+if errorlevel 1 goto build-node-weak-failed
+goto run-tests
+
+:build-node-weak-failed
+echo Failed to build node-weak.
+goto exit
+
+:run-tests
 echo running 'python tools/test.py %test_args%'
 python tools/test.py %test_args%
-
 if "%test%"=="test" goto jslint
-
 goto exit
 
 :create-msvs-files-failed