OLD | NEW |
---|---|
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 23 matching lines...) Expand all Loading... | |
34 | 34 |
35 namespace v8 { | 35 namespace v8 { |
36 namespace internal { | 36 namespace internal { |
37 | 37 |
38 | 38 |
39 ObjectGroup::~ObjectGroup() { | 39 ObjectGroup::~ObjectGroup() { |
40 if (info_ != NULL) info_->Dispose(); | 40 if (info_ != NULL) info_->Dispose(); |
41 } | 41 } |
42 | 42 |
43 | 43 |
44 class GlobalHandles::Node : public Malloced { | 44 class GlobalHandles::Node { |
45 public: | 45 public: |
46 // State transition diagram: | |
47 // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE } | |
48 enum State { | |
49 FREE, | |
50 NORMAL, // Normal global handle. | |
51 WEAK, // Flagged as weak but not yet finalized. | |
52 PENDING, // Has been recognized as only reachable by weak handles. | |
53 NEAR_DEATH // Callback has informed the handle is near death. | |
54 }; | |
46 | 55 |
47 void Initialize(Object* object) { | 56 // Maps handle location (slot) to the containing node. |
48 // Set the initial value of the handle. | 57 static Node* FromLocation(Object** location) { |
58 ASSERT(OFFSET_OF(Node, object_) == 0); | |
59 return reinterpret_cast<Node*>(location); | |
60 } | |
61 | |
62 Node() {} | |
63 | |
64 #ifdef DEBUG | |
65 ~Node() { | |
66 // TODO(bug!): if it's a weak handle we should have invoked its callback. | |
Vyacheslav Egorov (Chromium)
2011/06/06 12:59:22
file a bug?
Vitaly Repeshko
2011/06/06 15:19:43
Filed http://code.google.com/p/v8/issues/detail?id
| |
67 // Zap the values for eager trapping. | |
68 object_ = NULL; | |
69 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; | |
70 index_ = 0; | |
71 independent_ = false; | |
72 in_new_space_list_ = false; | |
73 parameter_or_next_free_.next_free = NULL; | |
74 callback_ = NULL; | |
75 } | |
76 #endif | |
77 | |
78 void Initialize(int index, Node** first_free) { | |
79 index_ = static_cast<uint8_t>(index); | |
80 ASSERT(static_cast<int>(index_) == index); | |
81 state_ = FREE; | |
82 in_new_space_list_ = false; | |
83 parameter_or_next_free_.next_free = *first_free; | |
84 *first_free = this; | |
85 } | |
86 | |
87 void Acquire(Object* object, GlobalHandles* global_handles) { | |
88 ASSERT(state_ == FREE); | |
49 object_ = object; | 89 object_ = object; |
50 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; | 90 class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; |
51 independent_ = false; | 91 independent_ = false; |
52 state_ = NORMAL; | 92 state_ = NORMAL; |
53 parameter_or_next_free_.parameter = NULL; | 93 parameter_or_next_free_.parameter = NULL; |
54 callback_ = NULL; | 94 callback_ = NULL; |
95 IncreaseBlockUses(global_handles); | |
55 } | 96 } |
56 | 97 |
57 Node() { | 98 void Release(GlobalHandles* global_handles) { |
58 state_ = DESTROYED; | 99 ASSERT(state_ != FREE); |
59 } | 100 if (IsWeakRetainer()) { |
60 | |
61 explicit Node(Object* object) { | |
62 Initialize(object); | |
63 // Initialize link structure. | |
64 next_ = NULL; | |
65 } | |
66 | |
67 ~Node() { | |
68 if (state_ != DESTROYED) Destroy(Isolate::Current()->global_handles()); | |
69 #ifdef DEBUG | |
70 // Zap the values for eager trapping. | |
71 object_ = NULL; | |
72 next_ = NULL; | |
73 parameter_or_next_free_.next_free = NULL; | |
74 #endif | |
75 } | |
76 | |
77 void Destroy(GlobalHandles* global_handles) { | |
78 if (state_ == WEAK || IsNearDeath()) { | |
79 global_handles->number_of_weak_handles_--; | 101 global_handles->number_of_weak_handles_--; |
80 if (object_->IsJSGlobalObject()) { | 102 if (object_->IsJSGlobalObject()) { |
81 global_handles->number_of_global_object_weak_handles_--; | 103 global_handles->number_of_global_object_weak_handles_--; |
82 } | 104 } |
83 } | 105 } |
84 state_ = DESTROYED; | 106 state_ = FREE; |
107 parameter_or_next_free_.next_free = global_handles->first_free_; | |
108 global_handles->first_free_ = this; | |
109 DecreaseBlockUses(global_handles); | |
85 } | 110 } |
86 | 111 |
87 // Accessors for next_. | 112 // Object slot accessors. |
88 Node* next() { return next_; } | 113 Object* object() const { return object_; } |
89 void set_next(Node* value) { next_ = value; } | 114 Object** location() { return &object_; } |
90 Node** next_addr() { return &next_; } | 115 Handle<Object> handle() { return Handle<Object>(location()); } |
116 | |
117 // Wrapper class ID accessors. | |
118 bool has_wrapper_class_id() const { | |
119 return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId; | |
120 } | |
121 uint16_t wrapper_class_id() const { return class_id_; } | |
122 void set_wrapper_class_id(uint16_t class_id) { | |
123 class_id_ = class_id; | |
124 } | |
125 | |
126 // State accessors. | |
127 | |
128 State state() const { return state_; } | |
129 | |
130 bool IsNearDeath() const { | |
131 // Check for PENDING to ensure correct answer when processing callbacks. | |
132 return state_ == PENDING || state_ == NEAR_DEATH; | |
133 } | |
134 | |
135 bool IsWeak() const { return state_ == WEAK; } | |
136 | |
137 bool IsRetainer() const { return state_ != FREE; } | |
138 | |
139 bool IsStrongRetainer() const { return state_ == NORMAL; } | |
140 | |
141 bool IsWeakRetainer() const { | |
142 return state_ == WEAK || state_ == PENDING || state_ == NEAR_DEATH; | |
143 } | |
144 | |
145 void MarkPending() { | |
146 ASSERT(state_ == WEAK); | |
147 state_ = PENDING; | |
148 } | |
149 | |
150 // Independent flag accessors. | |
151 void MarkIndependent() { | |
152 ASSERT(state_ != FREE); | |
153 independent_ = true; | |
154 } | |
155 bool is_independent() const { return independent_; } | |
156 | |
157 // In-new-space-list flag accessors. | |
158 void set_in_new_space_list(bool v) { in_new_space_list_ = v; } | |
159 bool is_in_new_space_list() const { return in_new_space_list_; } | |
160 | |
161 // Callback accessor. | |
162 WeakReferenceCallback callback() { return callback_; } | |
163 | |
164 // Callback parameter accessors. | |
165 void set_parameter(void* parameter) { | |
166 ASSERT(state_ != FREE); | |
167 parameter_or_next_free_.parameter = parameter; | |
168 } | |
169 void* parameter() const { | |
170 ASSERT(state_ != FREE); | |
171 return parameter_or_next_free_.parameter; | |
172 } | |
91 | 173 |
92 // Accessors for next free node in the free list. | 174 // Accessors for next free node in the free list. |
93 Node* next_free() { | 175 Node* next_free() { |
94 ASSERT(state_ == DESTROYED); | 176 ASSERT(state_ == FREE); |
95 return parameter_or_next_free_.next_free; | 177 return parameter_or_next_free_.next_free; |
96 } | 178 } |
97 void set_next_free(Node* value) { | 179 void set_next_free(Node* value) { |
98 ASSERT(state_ == DESTROYED); | 180 ASSERT(state_ == FREE); |
99 parameter_or_next_free_.next_free = value; | 181 parameter_or_next_free_.next_free = value; |
100 } | 182 } |
101 | 183 |
102 // Returns a link from the handle. | 184 void MakeWeak(GlobalHandles* global_handles, |
103 static Node* FromLocation(Object** location) { | 185 void* parameter, |
104 ASSERT(OFFSET_OF(Node, object_) == 0); | |
105 return reinterpret_cast<Node*>(location); | |
106 } | |
107 | |
108 // Returns the handle. | |
109 Handle<Object> handle() { return Handle<Object>(&object_); } | |
110 | |
111 // Make this handle weak. | |
112 void MakeWeak(GlobalHandles* global_handles, void* parameter, | |
113 WeakReferenceCallback callback) { | 186 WeakReferenceCallback callback) { |
114 LOG(global_handles->isolate(), | 187 ASSERT(state_ != FREE); |
115 HandleEvent("GlobalHandle::MakeWeak", handle().location())); | 188 if (!IsWeakRetainer()) { |
116 ASSERT(state_ != DESTROYED); | |
117 if (state_ != WEAK && !IsNearDeath()) { | |
118 global_handles->number_of_weak_handles_++; | 189 global_handles->number_of_weak_handles_++; |
119 if (object_->IsJSGlobalObject()) { | 190 if (object_->IsJSGlobalObject()) { |
120 global_handles->number_of_global_object_weak_handles_++; | 191 global_handles->number_of_global_object_weak_handles_++; |
121 } | 192 } |
122 } | 193 } |
123 state_ = WEAK; | 194 state_ = WEAK; |
124 set_parameter(parameter); | 195 set_parameter(parameter); |
125 callback_ = callback; | 196 callback_ = callback; |
126 } | 197 } |
127 | 198 |
128 void ClearWeakness(GlobalHandles* global_handles) { | 199 void ClearWeakness(GlobalHandles* global_handles) { |
129 LOG(global_handles->isolate(), | 200 ASSERT(state_ != FREE); |
130 HandleEvent("GlobalHandle::ClearWeakness", handle().location())); | 201 if (IsWeakRetainer()) { |
131 ASSERT(state_ != DESTROYED); | |
132 if (state_ == WEAK || IsNearDeath()) { | |
133 global_handles->number_of_weak_handles_--; | 202 global_handles->number_of_weak_handles_--; |
134 if (object_->IsJSGlobalObject()) { | 203 if (object_->IsJSGlobalObject()) { |
135 global_handles->number_of_global_object_weak_handles_--; | 204 global_handles->number_of_global_object_weak_handles_--; |
136 } | 205 } |
137 } | 206 } |
138 state_ = NORMAL; | 207 state_ = NORMAL; |
139 set_parameter(NULL); | 208 set_parameter(NULL); |
140 } | 209 } |
141 | 210 |
142 void MarkIndependent(GlobalHandles* global_handles) { | |
143 LOG(global_handles->isolate(), | |
144 HandleEvent("GlobalHandle::MarkIndependent", handle().location())); | |
145 ASSERT(state_ != DESTROYED); | |
146 independent_ = true; | |
147 } | |
148 | |
149 bool IsNearDeath() { | |
150 // Check for PENDING to ensure correct answer when processing callbacks. | |
151 return state_ == PENDING || state_ == NEAR_DEATH; | |
152 } | |
153 | |
154 bool IsWeak() { | |
155 return state_ == WEAK; | |
156 } | |
157 | |
158 bool CanBeRetainer() { | |
159 return state_ != DESTROYED && state_ != NEAR_DEATH; | |
160 } | |
161 | |
162 void SetWrapperClassId(uint16_t class_id) { | |
163 class_id_ = class_id; | |
164 } | |
165 | |
166 // Returns the id for this weak handle. | |
167 void set_parameter(void* parameter) { | |
168 ASSERT(state_ != DESTROYED); | |
169 parameter_or_next_free_.parameter = parameter; | |
170 } | |
171 void* parameter() { | |
172 ASSERT(state_ != DESTROYED); | |
173 return parameter_or_next_free_.parameter; | |
174 } | |
175 | |
176 // Returns the callback for this weak handle. | |
177 WeakReferenceCallback callback() { return callback_; } | |
178 | |
179 bool PostGarbageCollectionProcessing(Isolate* isolate, | 211 bool PostGarbageCollectionProcessing(Isolate* isolate, |
180 GlobalHandles* global_handles) { | 212 GlobalHandles* global_handles) { |
181 if (state_ != Node::PENDING) return false; | 213 if (state_ != Node::PENDING) return false; |
182 LOG(isolate, HandleEvent("GlobalHandle::Processing", handle().location())); | |
183 WeakReferenceCallback func = callback(); | 214 WeakReferenceCallback func = callback(); |
184 if (func == NULL) { | 215 if (func == NULL) { |
185 Destroy(global_handles); | 216 Release(global_handles); |
186 return false; | 217 return false; |
187 } | 218 } |
188 void* par = parameter(); | 219 void* par = parameter(); |
189 state_ = NEAR_DEATH; | 220 state_ = NEAR_DEATH; |
190 set_parameter(NULL); | 221 set_parameter(NULL); |
191 | 222 |
192 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle()); | 223 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle()); |
193 { | 224 { |
194 // Forbid reuse of destroyed nodes as they might be already deallocated. | |
195 // It's fine though to reuse nodes that were destroyed in weak callback | |
196 // as those cannot be deallocated until we are back from the callback. | |
197 global_handles->set_first_free(NULL); | |
198 if (global_handles->first_deallocated()) { | |
199 global_handles->first_deallocated()->set_next(global_handles->head()); | |
200 } | |
201 // Check that we are not passing a finalized external string to | 225 // Check that we are not passing a finalized external string to |
202 // the callback. | 226 // the callback. |
203 ASSERT(!object_->IsExternalAsciiString() || | 227 ASSERT(!object_->IsExternalAsciiString() || |
204 ExternalAsciiString::cast(object_)->resource() != NULL); | 228 ExternalAsciiString::cast(object_)->resource() != NULL); |
205 ASSERT(!object_->IsExternalTwoByteString() || | 229 ASSERT(!object_->IsExternalTwoByteString() || |
206 ExternalTwoByteString::cast(object_)->resource() != NULL); | 230 ExternalTwoByteString::cast(object_)->resource() != NULL); |
207 // Leaving V8. | 231 // Leaving V8. |
208 VMState state(isolate, EXTERNAL); | 232 VMState state(isolate, EXTERNAL); |
209 func(object, par); | 233 func(object, par); |
210 } | 234 } |
211 // Absense of explicit cleanup or revival of weak handle | 235 // Absense of explicit cleanup or revival of weak handle |
212 // in most of the cases would lead to memory leak. | 236 // in most of the cases would lead to memory leak. |
213 ASSERT(state_ != NEAR_DEATH); | 237 ASSERT(state_ != NEAR_DEATH); |
214 return true; | 238 return true; |
215 } | 239 } |
216 | 240 |
217 // Place the handle address first to avoid offset computation. | 241 private: |
218 Object* object_; // Storage for object pointer. | 242 inline NodeBlock* FindBlock(); |
243 inline void IncreaseBlockUses(GlobalHandles* global_handles); | |
244 inline void DecreaseBlockUses(GlobalHandles* global_handles); | |
219 | 245 |
246 // Storage for object pointer. | |
247 // Placed first to avoid offset computation. | |
248 Object* object_; | |
249 | |
250 // Next word stores class_id, index, state, and independent. | |
251 // Note: the most aligned fields should go first. | |
252 | |
253 // Wrapper class ID. | |
220 uint16_t class_id_; | 254 uint16_t class_id_; |
221 | 255 |
222 // Transition diagram: | 256 // Index in the containing handle block. |
223 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED } | 257 uint8_t index_; |
224 enum State { | 258 |
225 NORMAL, // Normal global handle. | 259 // Need one more bit for MSVC as it treats enums as signed. |
226 WEAK, // Flagged as weak but not yet finalized. | 260 State state_ : 4; |
227 PENDING, // Has been recognized as only reachable by weak handles. | |
228 NEAR_DEATH, // Callback has informed the handle is near death. | |
229 DESTROYED | |
230 }; | |
231 State state_ : 4; // Need one more bit for MSVC as it treats enums as signed. | |
232 | 261 |
233 bool independent_ : 1; | 262 bool independent_ : 1; |
263 bool in_new_space_list_ : 1; | |
234 | 264 |
235 private: | |
236 // Handle specific callback. | 265 // Handle specific callback. |
237 WeakReferenceCallback callback_; | 266 WeakReferenceCallback callback_; |
238 // Provided data for callback. In DESTROYED state, this is used for | 267 |
268 // Provided data for callback. In FREE state, this is used for | |
239 // the free list link. | 269 // the free list link. |
240 union { | 270 union { |
241 void* parameter; | 271 void* parameter; |
242 Node* next_free; | 272 Node* next_free; |
243 } parameter_or_next_free_; | 273 } parameter_or_next_free_; |
244 | 274 |
245 // Linkage for the list. | 275 DISALLOW_COPY_AND_ASSIGN(Node); |
246 Node* next_; | |
247 | |
248 public: | |
249 TRACK_MEMORY("GlobalHandles::Node") | |
250 }; | 276 }; |
251 | 277 |
252 | 278 |
253 class GlobalHandles::Pool { | 279 class GlobalHandles::NodeBlock { |
254 public: | 280 public: |
255 Pool() { | 281 static const int kSize = 256; |
256 current_ = new Chunk(); | 282 |
257 current_->previous = NULL; | 283 explicit NodeBlock(NodeBlock* next) |
258 next_ = current_->nodes; | 284 : next_(next), used_nodes_(0), next_used_(NULL), prev_used_(NULL) {} |
259 limit_ = current_->nodes + kNodesPerChunk; | 285 |
286 void PutNodesOnFreeList(Node** first_free) { | |
287 for (int i = kSize - 1; i >= 0; --i) { | |
288 nodes_[i].Initialize(i, first_free); | |
260 } | 289 } |
290 } | |
261 | 291 |
262 ~Pool() { | 292 Node* node_at(int index) { |
263 if (current_ != NULL) { | 293 ASSERT(0 <= index && index < kSize); |
264 Release(); | 294 return &nodes_[index]; |
295 } | |
296 | |
297 void IncreaseUses(GlobalHandles* global_handles) { | |
298 ASSERT(used_nodes_ < kSize); | |
299 if (used_nodes_++ == 0) { | |
300 NodeBlock* old_first = global_handles->first_used_block_; | |
301 global_handles->first_used_block_ = this; | |
302 next_used_ = old_first; | |
303 prev_used_ = NULL; | |
304 if (old_first == NULL) return; | |
305 old_first->prev_used_ = this; | |
306 } | |
307 } | |
308 | |
309 void DecreaseUses(GlobalHandles* global_handles) { | |
310 ASSERT(used_nodes_ > 0); | |
311 if (--used_nodes_ == 0) { | |
312 if (next_used_ != NULL) next_used_->prev_used_ = prev_used_; | |
313 if (prev_used_ != NULL) prev_used_->next_used_ = next_used_; | |
314 if (this == global_handles->first_used_block_) { | |
315 global_handles->first_used_block_ = next_used_; | |
265 } | 316 } |
266 } | 317 } |
318 } | |
267 | 319 |
268 Node* Allocate() { | 320 // Next block in the list of all blocks. |
269 if (next_ < limit_) { | 321 NodeBlock* next() const { return next_; } |
270 return next_++; | |
271 } | |
272 return SlowAllocate(); | |
273 } | |
274 | 322 |
275 void Release() { | 323 // Next/previous block in the list of blocks with used nodes. |
276 Chunk* current = current_; | 324 NodeBlock* next_used() const { return next_used_; } |
277 ASSERT(current != NULL); // At least a single block must by allocated | 325 NodeBlock* prev_used() const { return prev_used_; } |
278 do { | |
279 Chunk* previous = current->previous; | |
280 delete current; | |
281 current = previous; | |
282 } while (current != NULL); | |
283 current_ = NULL; | |
284 next_ = limit_ = NULL; | |
285 } | |
286 | 326 |
287 private: | 327 private: |
288 static const int kNodesPerChunk = (1 << 12) - 1; | 328 Node nodes_[kSize]; |
289 struct Chunk : public Malloced { | 329 NodeBlock* const next_; |
290 Chunk* previous; | 330 int used_nodes_; |
291 Node nodes[kNodesPerChunk]; | 331 NodeBlock* next_used_; |
292 }; | 332 NodeBlock* prev_used_; |
293 | |
294 Node* SlowAllocate() { | |
295 Chunk* chunk = new Chunk(); | |
296 chunk->previous = current_; | |
297 current_ = chunk; | |
298 | |
299 Node* new_nodes = current_->nodes; | |
300 next_ = new_nodes + 1; | |
301 limit_ = new_nodes + kNodesPerChunk; | |
302 return new_nodes; | |
303 } | |
304 | |
305 Chunk* current_; | |
306 Node* next_; | |
307 Node* limit_; | |
308 }; | 333 }; |
309 | 334 |
310 | 335 |
336 GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() { | |
337 intptr_t ptr = reinterpret_cast<intptr_t>(this); | |
338 ptr = ptr - index_ * sizeof(Node); | |
339 NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr); | |
340 ASSERT(block->node_at(index_) == this); | |
341 return block; | |
342 } | |
343 | |
344 | |
345 void GlobalHandles::Node::IncreaseBlockUses(GlobalHandles* global_handles) { | |
346 FindBlock()->IncreaseUses(global_handles); | |
347 } | |
348 | |
349 | |
350 void GlobalHandles::Node::DecreaseBlockUses(GlobalHandles* global_handles) { | |
351 FindBlock()->DecreaseUses(global_handles); | |
352 } | |
353 | |
354 | |
355 class GlobalHandles::NodeIterator { | |
356 public: | |
357 explicit NodeIterator(GlobalHandles* global_handles) | |
358 : block_(global_handles->first_used_block_), | |
359 index_(0) {} | |
360 | |
361 bool done() const { return block_ == NULL; } | |
362 | |
363 Node* node() const { | |
364 ASSERT(!done()); | |
365 return block_->node_at(index_); | |
366 } | |
367 | |
368 void Advance() { | |
369 ASSERT(!done()); | |
370 if (++index_ < NodeBlock::kSize) return; | |
371 index_ = 0; | |
372 block_ = block_->next_used(); | |
373 } | |
374 | |
375 private: | |
376 NodeBlock* block_; | |
377 int index_; | |
378 | |
379 DISALLOW_COPY_AND_ASSIGN(NodeIterator); | |
380 }; | |
381 | |
382 | |
311 GlobalHandles::GlobalHandles(Isolate* isolate) | 383 GlobalHandles::GlobalHandles(Isolate* isolate) |
312 : isolate_(isolate), | 384 : isolate_(isolate), |
313 number_of_weak_handles_(0), | 385 number_of_weak_handles_(0), |
314 number_of_global_object_weak_handles_(0), | 386 number_of_global_object_weak_handles_(0), |
315 head_(NULL), | 387 first_block_(NULL), |
388 first_used_block_(NULL), | |
316 first_free_(NULL), | 389 first_free_(NULL), |
317 first_deallocated_(NULL), | 390 post_gc_processing_count_(0) {} |
318 pool_(new Pool()), | |
319 post_gc_processing_count_(0), | |
320 object_groups_(4) { | |
321 } | |
322 | 391 |
323 | 392 |
324 GlobalHandles::~GlobalHandles() { | 393 GlobalHandles::~GlobalHandles() { |
325 delete pool_; | 394 NodeBlock* block = first_block_; |
326 pool_ = 0; | 395 while (block != NULL) { |
396 NodeBlock* tmp = block->next(); | |
397 delete block; | |
398 block = tmp; | |
399 } | |
400 first_block_ = NULL; | |
327 } | 401 } |
328 | 402 |
329 | 403 |
330 Handle<Object> GlobalHandles::Create(Object* value) { | 404 Handle<Object> GlobalHandles::Create(Object* value) { |
331 isolate_->counters()->global_handles()->Increment(); | 405 isolate_->counters()->global_handles()->Increment(); |
332 Node* result; | 406 if (first_free_ == NULL) { |
333 if (first_free()) { | 407 first_block_ = new NodeBlock(first_block_); |
334 // Take the first node in the free list. | 408 first_block_->PutNodesOnFreeList(&first_free_); |
335 result = first_free(); | |
336 set_first_free(result->next_free()); | |
337 } else if (first_deallocated()) { | |
338 // Next try deallocated list | |
339 result = first_deallocated(); | |
340 set_first_deallocated(result->next_free()); | |
341 ASSERT(result->next() == head()); | |
342 set_head(result); | |
343 } else { | |
344 // Allocate a new node. | |
345 result = pool_->Allocate(); | |
346 result->set_next(head()); | |
347 set_head(result); | |
348 } | 409 } |
349 result->Initialize(value); | 410 ASSERT(first_free_ != NULL); |
411 // Take the first node in the free list. | |
412 Node* result = first_free_; | |
413 first_free_ = result->next_free(); | |
414 result->Acquire(value, this); | |
415 if (isolate_->heap()->InNewSpace(value) && | |
416 !result->is_in_new_space_list()) { | |
417 new_space_nodes_.Add(result); | |
418 result->set_in_new_space_list(true); | |
419 } | |
350 return result->handle(); | 420 return result->handle(); |
351 } | 421 } |
352 | 422 |
353 | 423 |
354 void GlobalHandles::Destroy(Object** location) { | 424 void GlobalHandles::Destroy(Object** location) { |
355 isolate_->counters()->global_handles()->Decrement(); | 425 isolate_->counters()->global_handles()->Decrement(); |
356 if (location == NULL) return; | 426 if (location == NULL) return; |
357 Node* node = Node::FromLocation(location); | 427 Node::FromLocation(location)->Release(this); |
358 node->Destroy(this); | |
359 // Link the destroyed. | |
360 node->set_next_free(first_free()); | |
361 set_first_free(node); | |
362 } | 428 } |
363 | 429 |
364 | 430 |
365 void GlobalHandles::MakeWeak(Object** location, void* parameter, | 431 void GlobalHandles::MakeWeak(Object** location, void* parameter, |
366 WeakReferenceCallback callback) { | 432 WeakReferenceCallback callback) { |
367 ASSERT(callback != NULL); | 433 ASSERT(callback != NULL); |
368 Node::FromLocation(location)->MakeWeak(this, parameter, callback); | 434 Node::FromLocation(location)->MakeWeak(this, parameter, callback); |
369 } | 435 } |
370 | 436 |
371 | 437 |
372 void GlobalHandles::ClearWeakness(Object** location) { | 438 void GlobalHandles::ClearWeakness(Object** location) { |
373 Node::FromLocation(location)->ClearWeakness(this); | 439 Node::FromLocation(location)->ClearWeakness(this); |
374 } | 440 } |
375 | 441 |
376 | 442 |
377 void GlobalHandles::MarkIndependent(Object** location) { | 443 void GlobalHandles::MarkIndependent(Object** location) { |
378 Node::FromLocation(location)->MarkIndependent(this); | 444 Node::FromLocation(location)->MarkIndependent(); |
379 } | 445 } |
380 | 446 |
381 | 447 |
382 bool GlobalHandles::IsNearDeath(Object** location) { | 448 bool GlobalHandles::IsNearDeath(Object** location) { |
383 return Node::FromLocation(location)->IsNearDeath(); | 449 return Node::FromLocation(location)->IsNearDeath(); |
384 } | 450 } |
385 | 451 |
386 | 452 |
387 bool GlobalHandles::IsWeak(Object** location) { | 453 bool GlobalHandles::IsWeak(Object** location) { |
388 return Node::FromLocation(location)->IsWeak(); | 454 return Node::FromLocation(location)->IsWeak(); |
389 } | 455 } |
390 | 456 |
391 | 457 |
392 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) { | 458 void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) { |
393 Node::FromLocation(location)->SetWrapperClassId(class_id); | 459 Node::FromLocation(location)->set_wrapper_class_id(class_id); |
394 } | 460 } |
395 | 461 |
396 | 462 |
397 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { | 463 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { |
398 // Traversal of GC roots in the global handle list that are marked as | 464 for (NodeIterator it(this); !it.done(); it.Advance()) { |
399 // WEAK, PENDING or NEAR_DEATH. | 465 if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location()); |
400 for (Node* current = head_; current != NULL; current = current->next()) { | |
401 if (current->state_ == Node::WEAK | |
402 || current->state_ == Node::PENDING | |
403 || current->state_ == Node::NEAR_DEATH) { | |
404 v->VisitPointer(¤t->object_); | |
405 } | |
406 } | 466 } |
407 } | 467 } |
408 | 468 |
409 | 469 |
410 void GlobalHandles::IterateWeakIndependentRoots(ObjectVisitor* v) { | 470 void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { |
411 // Traversal of GC roots in the global handle list that are independent | 471 for (int i = 0; i < new_space_nodes_.length(); ++i) { |
412 // and marked as WEAK, PENDING or NEAR_DEATH. | 472 Node* node = new_space_nodes_[i]; |
413 for (Node* current = head_; current != NULL; current = current->next()) { | 473 ASSERT(node->is_in_new_space_list()); |
414 if (!current->independent_) continue; | 474 if (node->is_independent() && node->IsWeakRetainer()) { |
415 if (current->state_ == Node::WEAK | 475 v->VisitPointer(node->location()); |
416 || current->state_ == Node::PENDING | |
417 || current->state_ == Node::NEAR_DEATH) { | |
418 v->VisitPointer(¤t->object_); | |
419 } | 476 } |
420 } | 477 } |
421 } | 478 } |
422 | 479 |
423 | 480 |
424 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f, | 481 void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f, |
425 WeakReferenceCallback callback) { | 482 WeakReferenceCallback callback) { |
426 for (Node* current = head_; current != NULL; current = current->next()) { | 483 for (NodeIterator it(this); !it.done(); it.Advance()) { |
427 if (current->IsWeak() && current->callback() == callback) { | 484 if (it.node()->IsWeak() && it.node()->callback() == callback) { |
428 f(current->object_, current->parameter()); | 485 f(it.node()->object(), it.node()->parameter()); |
429 } | 486 } |
430 } | 487 } |
431 } | 488 } |
432 | 489 |
433 | 490 |
434 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { | 491 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { |
435 for (Node* current = head_; current != NULL; current = current->next()) { | 492 for (NodeIterator it(this); !it.done(); it.Advance()) { |
436 if (current->state_ == Node::WEAK) { | 493 if (it.node()->IsWeak() && f(it.node()->location())) { |
437 if (f(¤t->object_)) { | 494 it.node()->MarkPending(); |
438 current->state_ = Node::PENDING; | |
439 LOG(isolate_, | |
440 HandleEvent("GlobalHandle::Pending", current->handle().location())); | |
441 } | |
442 } | 495 } |
443 } | 496 } |
444 } | 497 } |
445 | 498 |
446 | 499 |
447 void GlobalHandles::IdentifyWeakIndependentHandles(WeakSlotCallbackWithHeap f) { | 500 void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles( |
448 for (Node* current = head_; current != NULL; current = current->next()) { | 501 WeakSlotCallbackWithHeap f) { |
449 if (current->state_ == Node::WEAK && current->independent_) { | 502 for (int i = 0; i < new_space_nodes_.length(); ++i) { |
450 if (f(isolate_->heap(), ¤t->object_)) { | 503 Node* node = new_space_nodes_[i]; |
451 current->state_ = Node::PENDING; | 504 ASSERT(node->is_in_new_space_list()); |
452 LOG(isolate_, | 505 if (node->is_independent() && node->IsWeak() && |
453 HandleEvent("GlobalHandle::Pending", current->handle().location())); | 506 f(isolate_->heap(), node->location())) { |
454 } | 507 node->MarkPending(); |
455 } | 508 } |
456 } | 509 } |
457 } | 510 } |
458 | 511 |
459 | 512 |
460 bool GlobalHandles::PostGarbageCollectionProcessing( | 513 bool GlobalHandles::PostGarbageCollectionProcessing( |
461 GarbageCollector collector) { | 514 GarbageCollector collector) { |
462 // Process weak global handle callbacks. This must be done after the | 515 // Process weak global handle callbacks. This must be done after the |
463 // GC is completely done, because the callbacks may invoke arbitrary | 516 // GC is completely done, because the callbacks may invoke arbitrary |
464 // API functions. | 517 // API functions. |
465 // At the same time deallocate all DESTROYED nodes. | |
466 ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC); | 518 ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC); |
467 const int initial_post_gc_processing_count = ++post_gc_processing_count_; | 519 const int initial_post_gc_processing_count = ++post_gc_processing_count_; |
468 bool next_gc_likely_to_collect_more = false; | 520 bool next_gc_likely_to_collect_more = false; |
469 Node** p = &head_; | 521 if (collector == SCAVENGER) { |
470 while (*p != NULL) { | 522 for (int i = 0; i < new_space_nodes_.length(); ++i) { |
471 // Skip dependent handles. Their weak callbacks might expect to be | 523 Node* node = new_space_nodes_[i]; |
472 // called between two global garbage collection callbacks which | 524 ASSERT(node->is_in_new_space_list()); |
473 // are not called for minor collections. | 525 // Skip dependent handles. Their weak callbacks might expect to be |
474 if (collector == SCAVENGER && !(*p)->independent_) { | 526 // called between two global garbage collection callbacks which |
475 p = (*p)->next_addr(); | 527 // are not called for minor collections. |
476 continue; | 528 if (!node->is_independent()) continue; |
477 } | 529 if (node->PostGarbageCollectionProcessing(isolate_, this)) { |
478 | 530 if (initial_post_gc_processing_count != post_gc_processing_count_) { |
479 if ((*p)->PostGarbageCollectionProcessing(isolate_, this)) { | 531 // Weak callback triggered another GC and another round of |
480 if (initial_post_gc_processing_count != post_gc_processing_count_) { | 532 // PostGarbageCollection processing. The current node might |
481 // Weak callback triggered another GC and another round of | 533 // have been deleted in that round, so we need to bail out (or |
482 // PostGarbageCollection processing. The current node might | 534 // restart the processing). |
483 // have been deleted in that round, so we need to bail out (or | 535 return next_gc_likely_to_collect_more; |
484 // restart the processing). | 536 } |
485 break; | 537 } |
538 if (!node->IsRetainer()) { | |
539 next_gc_likely_to_collect_more = true; | |
486 } | 540 } |
487 } | 541 } |
488 if ((*p)->state_ == Node::DESTROYED) { | 542 } else { |
489 // Delete the link. | 543 for (NodeIterator it(this); !it.done(); it.Advance()) { |
490 Node* node = *p; | 544 if (it.node()->PostGarbageCollectionProcessing(isolate_, this)) { |
491 *p = node->next(); // Update the link. | 545 if (initial_post_gc_processing_count != post_gc_processing_count_) { |
492 if (first_deallocated()) { | 546 // See the comment above. |
493 first_deallocated()->set_next(node); | 547 return next_gc_likely_to_collect_more; |
548 } | |
494 } | 549 } |
495 node->set_next_free(first_deallocated()); | 550 if (!it.node()->IsRetainer()) { |
496 set_first_deallocated(node); | 551 next_gc_likely_to_collect_more = true; |
497 next_gc_likely_to_collect_more = true; | 552 } |
498 } else { | |
499 p = (*p)->next_addr(); | |
500 } | 553 } |
501 } | 554 } |
502 set_first_free(NULL); | 555 // Update the list of new space nodes. |
503 if (first_deallocated()) { | 556 int last = 0; |
504 first_deallocated()->set_next(head()); | 557 for (int i = 0; i < new_space_nodes_.length(); ++i) { |
558 Node* node = new_space_nodes_[i]; | |
559 ASSERT(node->is_in_new_space_list()); | |
560 if (node->IsRetainer() && isolate_->heap()->InNewSpace(node->object())) { | |
561 new_space_nodes_[last++] = node; | |
562 } else { | |
563 node->set_in_new_space_list(false); | |
564 } | |
505 } | 565 } |
506 | 566 new_space_nodes_.Rewind(last); |
507 return next_gc_likely_to_collect_more; | 567 return next_gc_likely_to_collect_more; |
508 } | 568 } |
509 | 569 |
510 | 570 |
511 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) { | 571 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) { |
512 // Traversal of global handles marked as NORMAL. | 572 for (NodeIterator it(this); !it.done(); it.Advance()) { |
513 for (Node* current = head_; current != NULL; current = current->next()) { | 573 if (it.node()->IsStrongRetainer()) { |
514 if (current->state_ == Node::NORMAL) { | 574 v->VisitPointer(it.node()->location()); |
515 v->VisitPointer(¤t->object_); | |
516 } | 575 } |
517 } | 576 } |
518 } | 577 } |
519 | 578 |
520 | 579 |
521 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) { | 580 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) { |
522 for (Node* current = head_; current != NULL; current = current->next()) { | 581 for (NodeIterator it(this); !it.done(); it.Advance()) { |
523 if (current->state_ != Node::DESTROYED) { | 582 if (it.node()->IsRetainer()) { |
524 v->VisitPointer(¤t->object_); | 583 v->VisitPointer(it.node()->location()); |
525 } | 584 } |
526 } | 585 } |
527 } | 586 } |
528 | 587 |
529 | 588 |
530 void GlobalHandles::IterateStrongAndDependentRoots(ObjectVisitor* v) { | 589 void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) { |
531 for (Node* current = head_; current != NULL; current = current->next()) { | 590 for (int i = 0; i < new_space_nodes_.length(); ++i) { |
532 if ((current->independent_ && current->state_ == Node::NORMAL) || | 591 Node* node = new_space_nodes_[i]; |
533 (!current->independent_ && current->state_ != Node::DESTROYED)) { | 592 if (node->IsStrongRetainer() || |
534 v->VisitPointer(¤t->object_); | 593 (node->IsWeakRetainer() && !node->is_independent())) { |
594 v->VisitPointer(node->location()); | |
535 } | 595 } |
536 } | 596 } |
537 } | 597 } |
538 | 598 |
539 | 599 |
540 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) { | 600 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) { |
541 for (Node* current = head_; current != NULL; current = current->next()) { | 601 for (NodeIterator it(this); !it.done(); it.Advance()) { |
542 if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId && | 602 if (it.node()->has_wrapper_class_id() && it.node()->IsRetainer()) { |
543 current->CanBeRetainer()) { | 603 v->VisitEmbedderReference(it.node()->location(), |
544 v->VisitEmbedderReference(¤t->object_, current->class_id_); | 604 it.node()->wrapper_class_id()); |
545 } | 605 } |
546 } | 606 } |
547 } | 607 } |
548 | 608 |
549 | 609 |
550 void GlobalHandles::TearDown() { | |
551 // Reset all the lists. | |
552 set_head(NULL); | |
553 set_first_free(NULL); | |
554 set_first_deallocated(NULL); | |
555 pool_->Release(); | |
556 } | |
557 | |
558 | |
559 void GlobalHandles::RecordStats(HeapStats* stats) { | 610 void GlobalHandles::RecordStats(HeapStats* stats) { |
560 *stats->global_handle_count = 0; | 611 *stats->global_handle_count = 0; |
561 *stats->weak_global_handle_count = 0; | 612 *stats->weak_global_handle_count = 0; |
562 *stats->pending_global_handle_count = 0; | 613 *stats->pending_global_handle_count = 0; |
563 *stats->near_death_global_handle_count = 0; | 614 *stats->near_death_global_handle_count = 0; |
564 *stats->destroyed_global_handle_count = 0; | 615 *stats->free_global_handle_count = 0; |
565 for (Node* current = head_; current != NULL; current = current->next()) { | 616 for (NodeIterator it(this); !it.done(); it.Advance()) { |
566 *stats->global_handle_count += 1; | 617 *stats->global_handle_count += 1; |
567 if (current->state_ == Node::WEAK) { | 618 if (it.node()->state() == Node::WEAK) { |
568 *stats->weak_global_handle_count += 1; | 619 *stats->weak_global_handle_count += 1; |
569 } else if (current->state_ == Node::PENDING) { | 620 } else if (it.node()->state() == Node::PENDING) { |
570 *stats->pending_global_handle_count += 1; | 621 *stats->pending_global_handle_count += 1; |
571 } else if (current->state_ == Node::NEAR_DEATH) { | 622 } else if (it.node()->state() == Node::NEAR_DEATH) { |
572 *stats->near_death_global_handle_count += 1; | 623 *stats->near_death_global_handle_count += 1; |
573 } else if (current->state_ == Node::DESTROYED) { | 624 } else if (it.node()->state() == Node::FREE) { |
574 *stats->destroyed_global_handle_count += 1; | 625 *stats->free_global_handle_count += 1; |
575 } | 626 } |
576 } | 627 } |
577 } | 628 } |
578 | 629 |
579 #ifdef DEBUG | 630 #ifdef DEBUG |
580 | 631 |
581 void GlobalHandles::PrintStats() { | 632 void GlobalHandles::PrintStats() { |
582 int total = 0; | 633 int total = 0; |
583 int weak = 0; | 634 int weak = 0; |
584 int pending = 0; | 635 int pending = 0; |
585 int near_death = 0; | 636 int near_death = 0; |
586 int destroyed = 0; | 637 int destroyed = 0; |
587 | 638 |
588 for (Node* current = head_; current != NULL; current = current->next()) { | 639 for (NodeIterator it(this); !it.done(); it.Advance()) { |
589 total++; | 640 total++; |
590 if (current->state_ == Node::WEAK) weak++; | 641 if (it.node()->state() == Node::WEAK) weak++; |
591 if (current->state_ == Node::PENDING) pending++; | 642 if (it.node()->state() == Node::PENDING) pending++; |
592 if (current->state_ == Node::NEAR_DEATH) near_death++; | 643 if (it.node()->state() == Node::NEAR_DEATH) near_death++; |
593 if (current->state_ == Node::DESTROYED) destroyed++; | 644 if (it.node()->state() == Node::FREE) destroyed++; |
594 } | 645 } |
595 | 646 |
596 PrintF("Global Handle Statistics:\n"); | 647 PrintF("Global Handle Statistics:\n"); |
597 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total); | 648 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total); |
598 PrintF(" # weak = %d\n", weak); | 649 PrintF(" # weak = %d\n", weak); |
599 PrintF(" # pending = %d\n", pending); | 650 PrintF(" # pending = %d\n", pending); |
600 PrintF(" # near_death = %d\n", near_death); | 651 PrintF(" # near_death = %d\n", near_death); |
601 PrintF(" # destroyed = %d\n", destroyed); | 652 PrintF(" # free = %d\n", destroyed); |
602 PrintF(" # total = %d\n", total); | 653 PrintF(" # total = %d\n", total); |
603 } | 654 } |
604 | 655 |
605 void GlobalHandles::Print() { | 656 void GlobalHandles::Print() { |
606 PrintF("Global handles:\n"); | 657 PrintF("Global handles:\n"); |
607 for (Node* current = head_; current != NULL; current = current->next()) { | 658 for (NodeIterator it(this); !it.done(); it.Advance()) { |
608 PrintF(" handle %p to %p (weak=%d)\n", | 659 PrintF(" handle %p to %p%s\n", |
609 reinterpret_cast<void*>(current->handle().location()), | 660 reinterpret_cast<void*>(it.node()->location()), |
610 reinterpret_cast<void*>(*current->handle()), | 661 reinterpret_cast<void*>(it.node()->object()), |
611 current->state_ == Node::WEAK); | 662 it.node()->IsWeak() ? " (weak)" : ""); |
612 } | 663 } |
613 } | 664 } |
614 | 665 |
615 #endif | 666 #endif |
616 | 667 |
617 | 668 |
618 | 669 |
619 void GlobalHandles::AddObjectGroup(Object*** handles, | 670 void GlobalHandles::AddObjectGroup(Object*** handles, |
620 size_t length, | 671 size_t length, |
621 v8::RetainedObjectInfo* info) { | 672 v8::RetainedObjectInfo* info) { |
622 #ifdef DEBUG | 673 #ifdef DEBUG |
623 for (size_t i = 0; i < length; ++i) { | 674 for (size_t i = 0; i < length; ++i) { |
624 ASSERT(!Node::FromLocation(handles[i])->independent_); | 675 ASSERT(!Node::FromLocation(handles[i])->is_independent()); |
625 } | 676 } |
626 #endif | 677 #endif |
627 if (length == 0) { | 678 if (length == 0) { |
628 if (info != NULL) info->Dispose(); | 679 if (info != NULL) info->Dispose(); |
629 return; | 680 return; |
630 } | 681 } |
631 object_groups_.Add(ObjectGroup::New(handles, length, info)); | 682 object_groups_.Add(ObjectGroup::New(handles, length, info)); |
632 } | 683 } |
633 | 684 |
634 | 685 |
635 void GlobalHandles::AddImplicitReferences(HeapObject** parent, | 686 void GlobalHandles::AddImplicitReferences(HeapObject** parent, |
636 Object*** children, | 687 Object*** children, |
637 size_t length) { | 688 size_t length) { |
638 #ifdef DEBUG | 689 #ifdef DEBUG |
639 ASSERT(!Node::FromLocation(BitCast<Object**>(parent))->independent_); | 690 ASSERT(!Node::FromLocation(BitCast<Object**>(parent))->is_independent()); |
640 for (size_t i = 0; i < length; ++i) { | 691 for (size_t i = 0; i < length; ++i) { |
641 ASSERT(!Node::FromLocation(children[i])->independent_); | 692 ASSERT(!Node::FromLocation(children[i])->is_independent()); |
642 } | 693 } |
643 #endif | 694 #endif |
644 if (length == 0) return; | 695 if (length == 0) return; |
645 implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length)); | 696 implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length)); |
646 } | 697 } |
647 | 698 |
648 | 699 |
649 void GlobalHandles::RemoveObjectGroups() { | 700 void GlobalHandles::RemoveObjectGroups() { |
650 for (int i = 0; i < object_groups_.length(); i++) { | 701 for (int i = 0; i < object_groups_.length(); i++) { |
651 object_groups_.at(i)->Dispose(); | 702 object_groups_.at(i)->Dispose(); |
652 } | 703 } |
653 object_groups_.Clear(); | 704 object_groups_.Clear(); |
654 } | 705 } |
655 | 706 |
656 | 707 |
657 void GlobalHandles::RemoveImplicitRefGroups() { | 708 void GlobalHandles::RemoveImplicitRefGroups() { |
658 for (int i = 0; i < implicit_ref_groups_.length(); i++) { | 709 for (int i = 0; i < implicit_ref_groups_.length(); i++) { |
659 implicit_ref_groups_.at(i)->Dispose(); | 710 implicit_ref_groups_.at(i)->Dispose(); |
660 } | 711 } |
661 implicit_ref_groups_.Clear(); | 712 implicit_ref_groups_.Clear(); |
662 } | 713 } |
663 | 714 |
664 | 715 |
716 void GlobalHandles::TearDown() { | |
717 // TODO(bug!): invoke weak callbacks. | |
Vyacheslav Egorov (Chromium)
2011/06/06 12:59:22
File a bug?
Seems to be a huge change in the API.
Vitaly Repeshko
2011/06/06 15:19:43
See above. I agree. Let's do this in a separate ch
| |
718 } | |
719 | |
720 | |
665 } } // namespace v8::internal | 721 } } // namespace v8::internal |
OLD | NEW |