/* * 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" namespace { using node::FatalException; using v8::Array; using v8::Boolean; using v8::Exception; using v8::Function; using v8::FunctionTemplate; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::None; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyCallbackInfo; using v8::Value; using v8::String; using v8::TryCatch; typedef struct proxy_container { Persistent proxy; Persistent target; Persistent callbacks; } proxy_container; Persistent proxyClass; bool IsDead(Local proxy) { assert(proxy->InternalFieldCount() == 1); proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); return cont == NULL || cont->target.IsEmpty(); } Local Unwrap(Local proxy) { assert(!IsDead(proxy)); proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); Isolate* isolate = Isolate::GetCurrent(); return Local::New(isolate, cont->target); } Local GetCallbacks(Local proxy) { proxy_container *cont = reinterpret_cast( proxy->GetAlignedPointerFromInternalField(0)); assert(cont != NULL); Isolate* isolate = Isolate::GetCurrent(); return Local::New(isolate, cont->callbacks); } #define UNWRAP \ HandleScope scope(info.GetIsolate()); \ Local obj; \ const bool dead = IsDead(info.This()); \ if (!dead) obj = Unwrap(info.This()); \ void ThrowTypeError(Isolate* isolate, const char* message) { HandleScope scope(isolate); Local emessage = String::NewFromUtf8(isolate, message); isolate->ThrowException(v8::Exception::TypeError(emessage)); } void WeakNamedPropertyGetter(Local property, const PropertyCallbackInfo& info) { UNWRAP if (!dead) info.GetReturnValue().Set(obj->Get(property)); } void WeakNamedPropertySetter(Local property, Local value, const PropertyCallbackInfo& info) { UNWRAP if (!dead) obj->Set(property, value); } void WeakNamedPropertyQuery(Local property, const PropertyCallbackInfo& info) { info.GetReturnValue().Set(None); } void WeakNamedPropertyDeleter(Local property, const PropertyCallbackInfo& info) { UNWRAP info.GetReturnValue().Set(!dead && obj->Delete(property)); } void WeakIndexedPropertyGetter(uint32_t index, const PropertyCallbackInfo& info) { UNWRAP if (!dead) info.GetReturnValue().Set(obj->Get(index)); } void WeakIndexedPropertySetter(uint32_t index, Local value, const PropertyCallbackInfo& info) { UNWRAP if (!dead) obj->Set(index, value); } void WeakIndexedPropertyQuery(uint32_t index, const PropertyCallbackInfo& info) { info.GetReturnValue().Set(None); } void WeakIndexedPropertyDeleter(uint32_t index, const PropertyCallbackInfo& info) { UNWRAP info.GetReturnValue().Set(!dead && obj->Delete(index)); } void WeakPropertyEnumerator(const PropertyCallbackInfo& info) { UNWRAP info.GetReturnValue().Set(dead ? Array::New(0) : obj->GetPropertyNames()); } void AddCallback(Isolate* isolate, Local proxy, Local callback) { Local callbacks = GetCallbacks(proxy); callbacks->Set(Integer::New(isolate, callbacks->Length()), callback); } static void TargetCallback(const v8::WeakCallbackData& data) { Isolate* isolate = data.GetIsolate(); HandleScope scope(isolate); Local target = data.GetValue(); proxy_container* cont = data.GetParameter(); // invoke any listening callbacks Local callbacks = Local::New(isolate, cont->callbacks); uint32_t len = callbacks->Length(); Local argv[1]; argv[0] = target; for (uint32_t i=0; i cb = Local::Cast( callbacks->Get(Integer::New(isolate, i))); TryCatch try_catch; cb->Call(target, 1, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } } cont->proxy.Reset(); cont->target.Reset(); cont->callbacks.Reset(); delete cont; } void Create(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!args[0]->IsObject()) { ThrowTypeError(args.GetIsolate(), "Object expected"); return; } proxy_container *cont = new proxy_container; Isolate* isolate = args.GetIsolate(); Local tmpl = Local::New(isolate, proxyClass); Local proxy = tmpl->NewInstance(); proxy->SetAlignedPointerInInternalField(0, cont); cont->proxy.Reset(Isolate::GetCurrent(), proxy); cont->target.Reset(isolate, args[0].As()); cont->callbacks.Reset(isolate, Array::New(args.GetIsolate())); cont->target.SetWeak(cont, TargetCallback); if (args.Length() >= 2) { AddCallback(args.GetIsolate(), proxy, Local::Cast(args[1])); } args.GetReturnValue().Set(proxy); } /** * TODO: Make this better. */ bool isWeakRef (Local val) { return val->IsObject() && val->ToObject()->InternalFieldCount() == 1; } void IsWeakRef (const FunctionCallbackInfo& args) { args.GetReturnValue().Set(isWeakRef(args[0])); } void Get(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!isWeakRef(args[0])) { ThrowTypeError(args.GetIsolate(), "Weakref instance expected"); return; } Local proxy = args[0]->ToObject(); if (IsDead(proxy)) return; Local obj = Unwrap(proxy); args.GetReturnValue().Set(obj); } void IsNearDeath(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!isWeakRef(args[0])) { ThrowTypeError(args.GetIsolate(), "Weakref instance expected"); return; } Local proxy = args[0]->ToObject(); proxy_container *cont = static_cast( proxy->GetAlignedPointerFromInternalField(0)); assert(cont != NULL); args.GetReturnValue().Set(cont->target.IsNearDeath()); } void IsDead(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!isWeakRef(args[0])) { ThrowTypeError(args.GetIsolate(), "Weakref instance expected"); return; } Local proxy = args[0]->ToObject(); args.GetReturnValue().Set(IsDead(proxy)); } void AddCallback(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!isWeakRef(args[0])) { ThrowTypeError(args.GetIsolate(), "Weakref instance expected"); return; } Local proxy = args[0]->ToObject(); AddCallback(args.GetIsolate(), proxy, Local::Cast(args[1])); } void Callbacks(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); if (!isWeakRef(args[0])) { ThrowTypeError(args.GetIsolate(), "Weakref instance expected"); return; } Local proxy = args[0]->ToObject(); args.GetReturnValue().Set(GetCallbacks(proxy)); } void Initialize(Local target) { HandleScope scope(target->CreationContext()->GetIsolate()); Local tmpl = ObjectTemplate::New(); tmpl->SetNamedPropertyHandler(WeakNamedPropertyGetter, WeakNamedPropertySetter, WeakNamedPropertyQuery, WeakNamedPropertyDeleter, WeakPropertyEnumerator); tmpl->SetIndexedPropertyHandler(WeakIndexedPropertyGetter, WeakIndexedPropertySetter, WeakIndexedPropertyQuery, WeakIndexedPropertyDeleter, WeakPropertyEnumerator); tmpl->SetInternalFieldCount(1); proxyClass.Reset(Isolate::GetCurrent(), tmpl); 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);