|
| 1 | +// http://wiki.ecmascript.org/doku.php?id=harmony:ephemeron_tables |
| 2 | +// node-overload |
| 3 | +// @exports |
| 4 | +// OnCollect(Object,function(theObject)) |
| 5 | +// |
| 6 | + |
| 7 | +#include <v8.h> |
| 8 | +#include <map> |
| 9 | +#include <vector> |
| 10 | +using namespace v8; |
| 11 | +using namespace std; |
| 12 | + |
| 13 | +typedef pair<Persistent<Object>*, Persistent<Value>*> EphemeronPair; |
| 14 | +typedef vector<EphemeronPair > EphemeronVector; |
| 15 | +typedef map<int,EphemeronVector*> EphemeronMap; |
| 16 | + |
| 17 | +Persistent<ObjectTemplate> table_instance_template; |
| 18 | + |
| 19 | +void OurWeakReferenceCallback( |
| 20 | + Persistent<Value> object |
| 21 | + , void* parameter |
| 22 | + ) { |
| 23 | + HandleScope scope; |
| 24 | + Persistent<Function>* callback = (Persistent<Function>*)parameter; |
| 25 | + |
| 26 | + Handle<Value> values[0]; |
| 27 | + values[0] = object; |
| 28 | + //object.ClearWeak(); |
| 29 | + //puts("CB INVOKING"); |
| 30 | + (*callback)->Call(Context::GetCurrent()->Global(),1,values); |
| 31 | + //puts("OBJ BRANCHING"); |
| 32 | + if(object.IsNearDeath()) { |
| 33 | + object.Dispose(); |
| 34 | + delete callback; |
| 35 | + } |
| 36 | + //callback->ClearWeak(); |
| 37 | + //object.Dispose(); |
| 38 | + //delete callback; |
| 39 | + scope.Close(Undefined()); |
| 40 | +} |
| 41 | + |
| 42 | +void KeepingWeakReferenceCallback( |
| 43 | + Persistent<Value> callback |
| 44 | + , void* parameter |
| 45 | + ) { |
| 46 | + HandleScope scope; |
| 47 | + Persistent<Object>* object = (Persistent<Object>*)parameter; |
| 48 | + //puts("CB BRANCHING"); |
| 49 | + if(object->IsNearDeath()) { |
| 50 | + //puts("CB CLEARED"); |
| 51 | + callback.Dispose(); |
| 52 | + delete object; |
| 53 | + } |
| 54 | + else { |
| 55 | + //puts("CB REVIVED"); |
| 56 | + //Revive this object so long as there is a dependant weak reference |
| 57 | + callback.MakeWeak(parameter,KeepingWeakReferenceCallback); |
| 58 | + } |
| 59 | + scope.Close(Undefined()); |
| 60 | +} |
| 61 | + |
| 62 | +Handle<Value> Weak( |
| 63 | + const Arguments& args |
| 64 | + ) { |
| 65 | + HandleScope scope; |
| 66 | + Handle<Value> obj = args[0]; |
| 67 | + if(!obj->IsObject() || obj->IsNull() || obj->IsUndefined()) { |
| 68 | + return scope.Close(ThrowException(Exception::Error(String::New("First argument must be an object")))); |
| 69 | + } |
| 70 | + Handle<Value> onCollect = args[1]; |
| 71 | + if(onCollect->IsFunction()) { |
| 72 | + Persistent<Object>* persistent_obj = new Persistent<Object>(); |
| 73 | + *persistent_obj = Persistent<Object>::New(Handle<Object>::Cast(obj)); |
| 74 | + Persistent<Function>* persistent_cb = new Persistent<Function>(); |
| 75 | + *persistent_cb = Persistent<Function>::New(Handle<Function>::Cast(onCollect)); |
| 76 | + |
| 77 | + persistent_obj->MakeWeak( |
| 78 | + persistent_cb |
| 79 | + , OurWeakReferenceCallback |
| 80 | + ); |
| 81 | + //bind a weak ref of cb to the obj |
| 82 | + persistent_cb->MakeWeak( |
| 83 | + persistent_obj |
| 84 | + , KeepingWeakReferenceCallback |
| 85 | + ); |
| 86 | + return scope.Close(*persistent_obj); |
| 87 | + } |
| 88 | + else { |
| 89 | + return scope.Close(ThrowException(Exception::Error(String::New("Collect callback must be a function")))); |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +Handle<Object> WrapMap(EphemeronMap* obj,Handle<Object> table) { |
| 94 | + HandleScope scope; |
| 95 | + table->SetInternalField( |
| 96 | + 0, |
| 97 | + External::New(obj) |
| 98 | + ); |
| 99 | + return scope.Close(table); |
| 100 | +} |
| 101 | + |
| 102 | +EphemeronMap* UnwrapMap(Handle<Object> obj) { |
| 103 | + Handle<External> mymap_wrap = Handle<External>::Cast(obj->GetInternalField(0)); |
| 104 | + void* mymap_ptr = mymap_wrap->Value(); |
| 105 | + return static_cast<EphemeronMap*>( |
| 106 | + mymap_ptr |
| 107 | + ); |
| 108 | +} |
| 109 | + |
| 110 | +//Remove the object's pair from the map |
| 111 | +void EphemeronScrub( |
| 112 | + Persistent<Value> object |
| 113 | + , void* parameter |
| 114 | + ) { |
| 115 | + EphemeronMap* mymap = static_cast<EphemeronMap*>(parameter); |
| 116 | + Handle<Object> obj = Handle<Object>::Cast(object); |
| 117 | + int id = obj->GetIdentityHash(); |
| 118 | + EphemeronMap::iterator i = mymap->find(id); |
| 119 | + if(i != mymap->end()) { |
| 120 | + EphemeronVector* vector = (*i).second; |
| 121 | + EphemeronVector::iterator ii = vector->begin(); |
| 122 | + while(ii != vector->end()) { |
| 123 | + if(obj->StrictEquals(*((*ii).first))) { |
| 124 | + vector->erase(ii); |
| 125 | + if(vector->size()==0) { |
| 126 | + mymap->erase(id); |
| 127 | + } |
| 128 | + object.Dispose(); |
| 129 | + //puts("dispose + erase"); |
| 130 | + return; |
| 131 | + } |
| 132 | + ii++; |
| 133 | + } |
| 134 | + } |
| 135 | + //puts("dispose"); |
| 136 | + object.Dispose(); |
| 137 | +} |
| 138 | + |
| 139 | +Handle<Value> EphemeronInit( |
| 140 | + const Arguments& args |
| 141 | + ) { |
| 142 | + HandleScope scope; |
| 143 | + if(args.IsConstructCall()) { |
| 144 | + EphemeronMap* mymap = new EphemeronMap(); |
| 145 | + return scope.Close(WrapMap(mymap,args.This())); |
| 146 | + } |
| 147 | + return scope.Close(ThrowException(Exception::Error(String::New("Must be called through the 'new' operator.")))); |
| 148 | +} |
| 149 | + |
| 150 | +Handle<Value> EphemeronSet( |
| 151 | + const Arguments& args |
| 152 | + ) { |
| 153 | + HandleScope scope; |
| 154 | + Handle<Value> key = args[0]; |
| 155 | + if (key->IsUndefined() || key->IsNull()) { |
| 156 | + return scope.Close(ThrowException(Exception::Error(String::New("Key must be an Object")))); |
| 157 | + } |
| 158 | + Handle<Object> obj = key->ToObject(); |
| 159 | + Handle<Value> value = args[1]; |
| 160 | + |
| 161 | + EphemeronMap* mymap = UnwrapMap(args.This()); |
| 162 | + |
| 163 | + Persistent<Object>* obj_ptr = new Persistent<Object>(); |
| 164 | + *obj_ptr = Persistent<Object>::New(obj); |
| 165 | + obj_ptr->MakeWeak(mymap,EphemeronScrub); |
| 166 | + |
| 167 | + Persistent<Value>* value_ptr = new Persistent<Value>(); |
| 168 | + *value_ptr = Persistent<Value>::New(value); |
| 169 | + value_ptr->MakeWeak(obj_ptr,KeepingWeakReferenceCallback); |
| 170 | + |
| 171 | + int id = obj->GetIdentityHash(); |
| 172 | + |
| 173 | + EphemeronPair mypair = EphemeronPair(obj_ptr,value_ptr); |
| 174 | + EphemeronVector* vector = NULL; |
| 175 | + EphemeronMap::iterator i = mymap->find(id); |
| 176 | + |
| 177 | + if(i != mymap->end()) { |
| 178 | + vector = (*i).second; |
| 179 | + EphemeronVector::iterator ii = vector->begin(); |
| 180 | + int j = 0; |
| 181 | + while(ii != vector->end()) { |
| 182 | + EphemeronPair original_pair = *ii; |
| 183 | + if((*original_pair.first)->StrictEquals(obj)) { |
| 184 | + vector->at(j) = mypair; |
| 185 | + return scope.Close(obj); |
| 186 | + } |
| 187 | + ii++; |
| 188 | + j++; |
| 189 | + } |
| 190 | + i++; |
| 191 | + } |
| 192 | + else { |
| 193 | + vector = new EphemeronVector(); |
| 194 | + mymap->insert( pair<int,EphemeronVector*>(id,vector) ); |
| 195 | + } |
| 196 | + vector->push_back( mypair ); |
| 197 | + return scope.Close(obj); |
| 198 | +} |
| 199 | + |
| 200 | +Handle<Value> EphemeronGet( |
| 201 | + const Arguments& args |
| 202 | + ) { |
| 203 | + HandleScope scope; |
| 204 | + Handle<Value> key = args[0]; |
| 205 | + if (key->IsUndefined() || key->IsNull()) { |
| 206 | + return scope.Close(ThrowException(Exception::Error(String::New("Key must be an Object")))); |
| 207 | + } |
| 208 | + Handle<Object> obj = key->ToObject(); |
| 209 | + EphemeronMap* mymap = UnwrapMap(args.This()); |
| 210 | + EphemeronMap::iterator i = mymap->find(obj->GetIdentityHash()); |
| 211 | + if(i != mymap->end()) { |
| 212 | + EphemeronVector* vector = (*i).second; |
| 213 | + EphemeronVector::iterator ii = vector->begin(); |
| 214 | + while(ii != vector->end()) { |
| 215 | + EphemeronPair pair = *ii; |
| 216 | + if((*pair.first)->StrictEquals(obj)) { |
| 217 | + return scope.Close(*pair.second); |
| 218 | + } |
| 219 | + ii++; |
| 220 | + } |
| 221 | + i++; |
| 222 | + } |
| 223 | + return scope.Close(Undefined()); |
| 224 | +} |
| 225 | + |
| 226 | +Handle<Value> EphemeronHas( |
| 227 | + const Arguments& args |
| 228 | + ) { |
| 229 | + HandleScope scope; |
| 230 | + Handle<Value> key = args[0]; |
| 231 | + if (key->IsUndefined() || key->IsNull()) { |
| 232 | + return scope.Close(ThrowException(Exception::Error(String::New("Key must be an Object")))); |
| 233 | + } |
| 234 | + Handle<Object> obj = key->ToObject(); |
| 235 | + EphemeronMap* mymap = UnwrapMap(args.This()); |
| 236 | + EphemeronMap::iterator i = mymap->find(obj->GetIdentityHash()); |
| 237 | + if(i != mymap->end()) { |
| 238 | + EphemeronVector* vector = (*i).second; |
| 239 | + EphemeronVector::iterator ii = vector->begin(); |
| 240 | + while(ii != vector->end()) { |
| 241 | + if((*(*ii).first)->StrictEquals(obj)) { |
| 242 | + return scope.Close(Boolean::New(true)); |
| 243 | + } |
| 244 | + ii++; |
| 245 | + } |
| 246 | + i++; |
| 247 | + } |
| 248 | + return scope.Close(Boolean::New(false)); |
| 249 | +} |
| 250 | + |
| 251 | +Handle<Value> EphemeronKeys( |
| 252 | + const Arguments& args |
| 253 | + ) { |
| 254 | + HandleScope scope; |
| 255 | + EphemeronMap* mymap = UnwrapMap(args.This()); |
| 256 | + EphemeronMap::iterator i = mymap->begin(); |
| 257 | + int j = 0; |
| 258 | + Handle<Array> keys = Array::New(mymap->size()); |
| 259 | + while(i != mymap->end()) { |
| 260 | + EphemeronVector* vector = (*i).second; |
| 261 | + EphemeronVector::iterator ii = vector->begin(); |
| 262 | + while(ii != vector->end()) { |
| 263 | + keys->Set(j,*((*ii).first)); |
| 264 | + ii++; |
| 265 | + j++; |
| 266 | + } |
| 267 | + i++; |
| 268 | + } |
| 269 | + return scope.Close(keys); |
| 270 | +} |
0 commit comments