mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
8.7 KiB
316 lines
8.7 KiB
13 years ago
|
/*
|
||
|
* 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);
|