/* * Copyright (c) 2011, Ben Noordhuis * * 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 #include "v8.h" #include "node.h" using namespace v8; using namespace node; namespace { typedef struct proxy_container { Persistent proxy; Persistent target; Persistent callbacks; } proxy_container; Persistent proxyClass; bool IsDead(Handle proxy) { assert(proxy->InternalFieldCount() == 1); proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); return cont == NULL || cont->target.IsEmpty(); } Handle Unwrap(Handle proxy) { assert(!IsDead(proxy)); proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); return cont->target; } Handle GetCallbacks(Handle proxy) { proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); assert(cont != NULL); return cont->callbacks; } #define UNWRAP \ HandleScope scope; \ Handle obj; \ const bool dead = IsDead(info.This()); \ if (!dead) obj = Unwrap(info.This()); \ Handle WeakNamedPropertyGetter(Local property, const AccessorInfo& info) { UNWRAP return dead ? Local() : obj->Get(property); } Handle WeakNamedPropertySetter(Local property, Local value, const AccessorInfo& info) { UNWRAP if (!dead) obj->Set(property, value); return value; } Handle WeakNamedPropertyQuery(Local property, const AccessorInfo& info) { return HandleScope().Close(Integer::New(None)); } Handle WeakNamedPropertyDeleter(Local property, const AccessorInfo& info) { UNWRAP return Boolean::New(!dead && obj->Delete(property)); } Handle WeakIndexedPropertyGetter(uint32_t index, const AccessorInfo& info) { UNWRAP return dead ? Local() : obj->Get(index); } Handle WeakIndexedPropertySetter(uint32_t index, Local value, const AccessorInfo& info) { UNWRAP if (!dead) obj->Set(index, value); return value; } Handle WeakIndexedPropertyQuery(uint32_t index, const AccessorInfo& info) { return HandleScope().Close(Integer::New(None)); } Handle WeakIndexedPropertyDeleter(uint32_t index, const AccessorInfo& info) { UNWRAP return Boolean::New(!dead && obj->Delete(index)); } Handle WeakPropertyEnumerator(const AccessorInfo& info) { UNWRAP return HandleScope().Close(dead ? Array::New(0) : obj->GetPropertyNames()); } void AddCallback(Handle proxy, Handle callback) { Handle callbacks = GetCallbacks(proxy); callbacks->Set(Integer::New(callbacks->Length()), callback); } void TargetCallback(Isolate* isolate, Persistent* ptarget, void* arg) { HandleScope scope(isolate); Persistent target = *ptarget; assert(target.IsNearDeath()); proxy_container *cont = reinterpret_cast(arg); // invoke any listening callbacks uint32_t len = cont->callbacks->Length(); Handle argv[1]; argv[0] = target; for (uint32_t i=0; i cb = Handle::Cast( cont->callbacks->Get(Integer::New(i))); TryCatch try_catch; cb->Call(target, 1, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } } cont->proxy->SetAlignedPointerInInternalField(0, NULL); cont->proxy.Dispose(); cont->proxy.Clear(); cont->target.Dispose(); cont->target.Clear(); cont->callbacks.Dispose(); cont->callbacks.Clear(); free(cont); } Handle Create(const Arguments& args) { HandleScope scope; if (!args[0]->IsObject()) { Local message = String::New("Object expected"); return ThrowException(Exception::TypeError(message)); } proxy_container *cont = (proxy_container *) malloc(sizeof(proxy_container)); cont->target = Persistent::New(Isolate::GetCurrent(), args[0]->ToObject()); cont->callbacks = Persistent::New(Isolate::GetCurrent(), Array::New()); cont->proxy = Persistent::New(Isolate::GetCurrent(), proxyClass->NewInstance()); cont->proxy->SetAlignedPointerInInternalField(0, cont); cont->target.MakeWeak(Isolate::GetCurrent(), static_cast(cont), TargetCallback); if (args.Length() >= 2) { AddCallback(cont->proxy, Handle::Cast(args[1])); } return cont->proxy; } /** * TODO: Make this better. */ bool isWeakRef (Handle val) { return val->IsObject() && val->ToObject()->InternalFieldCount() == 1; } Handle IsWeakRef (const Arguments& args) { HandleScope scope; return Boolean::New(isWeakRef(args[0])); } Handle Get(const Arguments& args) { HandleScope scope; if (!isWeakRef(args[0])) { Local message = String::New("Weakref instance expected"); return ThrowException(Exception::TypeError(message)); } Local proxy = args[0]->ToObject(); const bool dead = IsDead(proxy); if (dead) return Undefined(); Handle obj = Unwrap(proxy); return scope.Close(obj); } Handle IsNearDeath(const Arguments& args) { HandleScope scope; if (!isWeakRef(args[0])) { Local message = String::New("Weakref instance expected"); return ThrowException(Exception::TypeError(message)); } Local proxy = args[0]->ToObject(); proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); assert(cont != NULL); Handle rtn = Boolean::New(cont->target.IsNearDeath()); return scope.Close(rtn); } Handle IsDead(const Arguments& args) { HandleScope scope; if (!isWeakRef(args[0])) { Local message = String::New("Weakref instance expected"); return ThrowException(Exception::TypeError(message)); } Local proxy = args[0]->ToObject(); const bool dead = IsDead(proxy); return Boolean::New(dead); } Handle AddCallback(const Arguments& args) { HandleScope scope; if (!isWeakRef(args[0])) { Local message = String::New("Weakref instance expected"); return ThrowException(Exception::TypeError(message)); } Local proxy = args[0]->ToObject(); AddCallback(proxy, Handle::Cast(args[1])); return Undefined(); } Handle Callbacks(const Arguments& args) { HandleScope scope; if (!isWeakRef(args[0])) { Local message = String::New("Weakref instance expected"); return ThrowException(Exception::TypeError(message)); } Local proxy = args[0]->ToObject(); return scope.Close(GetCallbacks(proxy)); } void Initialize(Handle target) { HandleScope scope; proxyClass = Persistent::New(Isolate::GetCurrent(), 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);