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