Skip to content
Browse files

test: bundle node-weak in test/gc so that it doesn't need to be downl…

…oaded
  • Loading branch information...
1 parent 35a1421 commit 6a8b5b36b4570b4e9468f4c4599f8f6513c35017 @TooTallNate TooTallNate committed with piscisaureus Jun 14, 2012
View
13 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
View
13 test/gc/node_modules/weak/LICENSE
@@ -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.
View
114 test/gc/node_modules/weak/README.md
@@ -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.
View
8 test/gc/node_modules/weak/binding.gyp
@@ -0,0 +1,8 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'weakref',
+ 'sources': [ 'src/weakref.cc' ]
+ }
+ ]
+}
View
9 test/gc/node_modules/weak/lib/weak.js
@@ -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]
+})
View
55 test/gc/node_modules/weak/package.json
@@ -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"
+}
View
315 test/gc/node_modules/weak/src/weakref.cc
@@ -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);
View
20 vcbuild.bat
@@ -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

0 comments on commit 6a8b5b3

Please sign in to comment.
Something went wrong with that request. Please try again.