blob: 18cdc5a3af5615e559e6d91faed474ef7430c8c5 [file] [log] [blame]
ager@chromium.org71daaf62009-04-01 07:22:49 +00001// Copyright 2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "api.h"
31#include "global-handles.h"
32
kasperl@chromium.orga5551262010-12-07 12:49:48 +000033#include "vm-state-inl.h"
34
kasperl@chromium.org71affb52009-05-26 05:44:31 +000035namespace v8 {
36namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037
38class GlobalHandles::Node : public Malloced {
39 public:
40
41 void Initialize(Object* object) {
42 // Set the initial value of the handle.
43 object_ = object;
44 state_ = NORMAL;
45 parameter_or_next_free_.parameter = NULL;
46 callback_ = NULL;
47 }
48
ager@chromium.org3811b432009-10-28 14:53:37 +000049 Node() {
50 state_ = DESTROYED;
51 }
52
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053 explicit Node(Object* object) {
54 Initialize(object);
55 // Initialize link structure.
56 next_ = NULL;
57 }
58
59 ~Node() {
60 if (state_ != DESTROYED) Destroy();
61#ifdef DEBUG
62 // Zap the values for eager trapping.
63 object_ = NULL;
64 next_ = NULL;
65 parameter_or_next_free_.next_free = NULL;
66#endif
67 }
68
69 void Destroy() {
70 if (state_ == WEAK || IsNearDeath()) {
71 GlobalHandles::number_of_weak_handles_--;
72 if (object_->IsJSGlobalObject()) {
73 GlobalHandles::number_of_global_object_weak_handles_--;
74 }
75 }
76 state_ = DESTROYED;
77 }
78
79 // Accessors for next_.
80 Node* next() { return next_; }
81 void set_next(Node* value) { next_ = value; }
82 Node** next_addr() { return &next_; }
83
84 // Accessors for next free node in the free list.
85 Node* next_free() {
86 ASSERT(state_ == DESTROYED);
87 return parameter_or_next_free_.next_free;
88 }
89 void set_next_free(Node* value) {
90 ASSERT(state_ == DESTROYED);
91 parameter_or_next_free_.next_free = value;
92 }
93
94 // Returns a link from the handle.
95 static Node* FromLocation(Object** location) {
96 ASSERT(OFFSET_OF(Node, object_) == 0);
97 return reinterpret_cast<Node*>(location);
98 }
99
100 // Returns the handle.
101 Handle<Object> handle() { return Handle<Object>(&object_); }
102
103 // Make this handle weak.
104 void MakeWeak(void* parameter, WeakReferenceCallback callback) {
105 LOG(HandleEvent("GlobalHandle::MakeWeak", handle().location()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000106 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000107 if (state_ != WEAK && !IsNearDeath()) {
108 GlobalHandles::number_of_weak_handles_++;
109 if (object_->IsJSGlobalObject()) {
110 GlobalHandles::number_of_global_object_weak_handles_++;
111 }
112 }
113 state_ = WEAK;
114 set_parameter(parameter);
115 callback_ = callback;
116 }
117
118 void ClearWeakness() {
119 LOG(HandleEvent("GlobalHandle::ClearWeakness", handle().location()));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000120 ASSERT(state_ != DESTROYED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000121 if (state_ == WEAK || IsNearDeath()) {
122 GlobalHandles::number_of_weak_handles_--;
123 if (object_->IsJSGlobalObject()) {
124 GlobalHandles::number_of_global_object_weak_handles_--;
125 }
126 }
127 state_ = NORMAL;
128 set_parameter(NULL);
129 }
130
131 bool IsNearDeath() {
132 // Check for PENDING to ensure correct answer when processing callbacks.
133 return state_ == PENDING || state_ == NEAR_DEATH;
134 }
135
136 bool IsWeak() {
137 return state_ == WEAK;
138 }
139
140 // Returns the id for this weak handle.
141 void set_parameter(void* parameter) {
142 ASSERT(state_ != DESTROYED);
143 parameter_or_next_free_.parameter = parameter;
144 }
145 void* parameter() {
146 ASSERT(state_ != DESTROYED);
147 return parameter_or_next_free_.parameter;
148 }
149
150 // Returns the callback for this weak handle.
151 WeakReferenceCallback callback() { return callback_; }
152
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000153 bool PostGarbageCollectionProcessing() {
154 if (state_ != Node::PENDING) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000155 LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000156 WeakReferenceCallback func = callback();
157 if (func == NULL) {
158 Destroy();
159 return false;
160 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000161 void* par = parameter();
162 state_ = NEAR_DEATH;
163 set_parameter(NULL);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000164
165 v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
166 {
167 // Forbid reuse of destroyed nodes as they might be already deallocated.
168 // It's fine though to reuse nodes that were destroyed in weak callback
169 // as those cannot be deallocated until we are back from the callback.
170 set_first_free(NULL);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000171 if (first_deallocated()) {
172 first_deallocated()->set_next(head());
173 }
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000174 // Check that we are not passing a finalized external string to
175 // the callback.
176 ASSERT(!object_->IsExternalAsciiString() ||
177 ExternalAsciiString::cast(object_)->resource() != NULL);
178 ASSERT(!object_->IsExternalTwoByteString() ||
179 ExternalTwoByteString::cast(object_)->resource() != NULL);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000180 // Leaving V8.
181 VMState state(EXTERNAL);
182 func(object, par);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000183 }
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000184 // Absense of explicit cleanup or revival of weak handle
185 // in most of the cases would lead to memory leak.
186 ASSERT(state_ != NEAR_DEATH);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000187 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000188 }
189
190 // Place the handle address first to avoid offset computation.
191 Object* object_; // Storage for object pointer.
192
193 // Transition diagram:
194 // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
195 enum State {
196 NORMAL, // Normal global handle.
197 WEAK, // Flagged as weak but not yet finalized.
198 PENDING, // Has been recognized as only reachable by weak handles.
199 NEAR_DEATH, // Callback has informed the handle is near death.
200 DESTROYED
201 };
202 State state_;
203
204 private:
205 // Handle specific callback.
206 WeakReferenceCallback callback_;
207 // Provided data for callback. In DESTROYED state, this is used for
208 // the free list link.
209 union {
210 void* parameter;
211 Node* next_free;
212 } parameter_or_next_free_;
213
214 // Linkage for the list.
215 Node* next_;
216
217 public:
218 TRACK_MEMORY("GlobalHandles::Node")
219};
220
221
ager@chromium.org3811b432009-10-28 14:53:37 +0000222class GlobalHandles::Pool BASE_EMBEDDED {
223 public:
224 Pool() {
225 current_ = new Chunk();
226 current_->previous = NULL;
227 next_ = current_->nodes;
228 limit_ = current_->nodes + kNodesPerChunk;
229 }
230
whesse@chromium.orge90029b2010-08-02 11:52:17 +0000231 ~Pool() {
232 if (current_ != NULL) {
233 Release();
234 }
235 }
236
ager@chromium.org3811b432009-10-28 14:53:37 +0000237 Node* Allocate() {
238 if (next_ < limit_) {
239 return next_++;
240 }
241 return SlowAllocate();
242 }
243
244 void Release() {
245 Chunk* current = current_;
246 ASSERT(current != NULL); // At least a single block must by allocated
247 do {
248 Chunk* previous = current->previous;
249 delete current;
250 current = previous;
251 } while (current != NULL);
252 current_ = NULL;
253 next_ = limit_ = NULL;
254 }
255
256 private:
257 static const int kNodesPerChunk = (1 << 12) - 1;
258 struct Chunk : public Malloced {
259 Chunk* previous;
260 Node nodes[kNodesPerChunk];
261 };
262
263 Node* SlowAllocate() {
264 Chunk* chunk = new Chunk();
265 chunk->previous = current_;
266 current_ = chunk;
267
268 Node* new_nodes = current_->nodes;
269 next_ = new_nodes + 1;
270 limit_ = new_nodes + kNodesPerChunk;
271 return new_nodes;
272 }
273
274 Chunk* current_;
275 Node* next_;
276 Node* limit_;
277};
278
279
280static GlobalHandles::Pool pool_;
281
282
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283Handle<Object> GlobalHandles::Create(Object* value) {
284 Counters::global_handles.Increment();
285 Node* result;
ager@chromium.org3811b432009-10-28 14:53:37 +0000286 if (first_free()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000287 // Take the first node in the free list.
288 result = first_free();
289 set_first_free(result->next_free());
ager@chromium.org3811b432009-10-28 14:53:37 +0000290 } else if (first_deallocated()) {
291 // Next try deallocated list
292 result = first_deallocated();
293 set_first_deallocated(result->next_free());
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000294 ASSERT(result->next() == head());
ager@chromium.org3811b432009-10-28 14:53:37 +0000295 set_head(result);
296 } else {
297 // Allocate a new node.
298 result = pool_.Allocate();
299 result->set_next(head());
300 set_head(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 }
ager@chromium.org3811b432009-10-28 14:53:37 +0000302 result->Initialize(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 return result->handle();
304}
305
306
307void GlobalHandles::Destroy(Object** location) {
308 Counters::global_handles.Decrement();
309 if (location == NULL) return;
310 Node* node = Node::FromLocation(location);
311 node->Destroy();
312 // Link the destroyed.
313 node->set_next_free(first_free());
314 set_first_free(node);
315}
316
317
318void GlobalHandles::MakeWeak(Object** location, void* parameter,
319 WeakReferenceCallback callback) {
320 ASSERT(callback != NULL);
321 Node::FromLocation(location)->MakeWeak(parameter, callback);
322}
323
324
325void GlobalHandles::ClearWeakness(Object** location) {
326 Node::FromLocation(location)->ClearWeakness();
327}
328
329
330bool GlobalHandles::IsNearDeath(Object** location) {
331 return Node::FromLocation(location)->IsNearDeath();
332}
333
334
335bool GlobalHandles::IsWeak(Object** location) {
336 return Node::FromLocation(location)->IsWeak();
337}
338
339
340void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
341 // Traversal of GC roots in the global handle list that are marked as
342 // WEAK or PENDING.
343 for (Node* current = head_; current != NULL; current = current->next()) {
344 if (current->state_ == Node::WEAK
345 || current->state_ == Node::PENDING
346 || current->state_ == Node::NEAR_DEATH) {
347 v->VisitPointer(&current->object_);
348 }
349 }
350}
351
352
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000353void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
354 WeakReferenceCallback callback) {
355 for (Node* current = head_; current != NULL; current = current->next()) {
356 if (current->IsWeak() && current->callback() == callback) {
357 f(current->object_, current->parameter());
358 }
359 }
360}
361
362
ager@chromium.org9085a012009-05-11 19:22:57 +0000363void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364 for (Node* current = head_; current != NULL; current = current->next()) {
365 if (current->state_ == Node::WEAK) {
366 if (f(&current->object_)) {
367 current->state_ = Node::PENDING;
368 LOG(HandleEvent("GlobalHandle::Pending", current->handle().location()));
369 }
370 }
371 }
372}
373
374
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000375int post_gc_processing_count = 0;
376
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000377bool GlobalHandles::PostGarbageCollectionProcessing() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000378 // Process weak global handle callbacks. This must be done after the
379 // GC is completely done, because the callbacks may invoke arbitrary
380 // API functions.
ager@chromium.org3811b432009-10-28 14:53:37 +0000381 // At the same time deallocate all DESTROYED nodes.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000382 ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000383 const int initial_post_gc_processing_count = ++post_gc_processing_count;
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000384 bool next_gc_likely_to_collect_more = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000385 Node** p = &head_;
386 while (*p != NULL) {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000387 if ((*p)->PostGarbageCollectionProcessing()) {
388 if (initial_post_gc_processing_count != post_gc_processing_count) {
389 // Weak callback triggered another GC and another round of
390 // PostGarbageCollection processing. The current node might
391 // have been deleted in that round, so we need to bail out (or
392 // restart the processing).
393 break;
394 }
395 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396 if ((*p)->state_ == Node::DESTROYED) {
397 // Delete the link.
398 Node* node = *p;
399 *p = node->next(); // Update the link.
ager@chromium.org3811b432009-10-28 14:53:37 +0000400 if (first_deallocated()) {
401 first_deallocated()->set_next(node);
402 }
403 node->set_next_free(first_deallocated());
404 set_first_deallocated(node);
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000405 next_gc_likely_to_collect_more = true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000406 } else {
407 p = (*p)->next_addr();
408 }
409 }
410 set_first_free(NULL);
ager@chromium.org3811b432009-10-28 14:53:37 +0000411 if (first_deallocated()) {
412 first_deallocated()->set_next(head());
413 }
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000414
415 return next_gc_likely_to_collect_more;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416}
417
418
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000419void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
420 // Traversal of global handles marked as NORMAL.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000421 for (Node* current = head_; current != NULL; current = current->next()) {
422 if (current->state_ == Node::NORMAL) {
423 v->VisitPointer(&current->object_);
424 }
425 }
426}
427
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000428
429void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
430 for (Node* current = head_; current != NULL; current = current->next()) {
431 if (current->state_ != Node::DESTROYED) {
432 v->VisitPointer(&current->object_);
433 }
434 }
435}
436
437
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000438void GlobalHandles::TearDown() {
ager@chromium.org3811b432009-10-28 14:53:37 +0000439 // Reset all the lists.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000440 set_head(NULL);
441 set_first_free(NULL);
ager@chromium.org3811b432009-10-28 14:53:37 +0000442 set_first_deallocated(NULL);
443 pool_.Release();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000444}
445
446
447int GlobalHandles::number_of_weak_handles_ = 0;
448int GlobalHandles::number_of_global_object_weak_handles_ = 0;
449
450GlobalHandles::Node* GlobalHandles::head_ = NULL;
451GlobalHandles::Node* GlobalHandles::first_free_ = NULL;
ager@chromium.org3811b432009-10-28 14:53:37 +0000452GlobalHandles::Node* GlobalHandles::first_deallocated_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000453
ager@chromium.org60121232009-12-03 11:25:37 +0000454void GlobalHandles::RecordStats(HeapStats* stats) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000455 *stats->global_handle_count = 0;
456 *stats->weak_global_handle_count = 0;
457 *stats->pending_global_handle_count = 0;
458 *stats->near_death_global_handle_count = 0;
459 *stats->destroyed_global_handle_count = 0;
ager@chromium.org60121232009-12-03 11:25:37 +0000460 for (Node* current = head_; current != NULL; current = current->next()) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000461 *stats->global_handle_count += 1;
ager@chromium.org60121232009-12-03 11:25:37 +0000462 if (current->state_ == Node::WEAK) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000463 *stats->weak_global_handle_count += 1;
ager@chromium.org60121232009-12-03 11:25:37 +0000464 } else if (current->state_ == Node::PENDING) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000465 *stats->pending_global_handle_count += 1;
ager@chromium.org60121232009-12-03 11:25:37 +0000466 } else if (current->state_ == Node::NEAR_DEATH) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000467 *stats->near_death_global_handle_count += 1;
ager@chromium.org60121232009-12-03 11:25:37 +0000468 } else if (current->state_ == Node::DESTROYED) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000469 *stats->destroyed_global_handle_count += 1;
ager@chromium.org60121232009-12-03 11:25:37 +0000470 }
471 }
472}
473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000474#ifdef DEBUG
475
476void GlobalHandles::PrintStats() {
477 int total = 0;
478 int weak = 0;
479 int pending = 0;
480 int near_death = 0;
481 int destroyed = 0;
482
483 for (Node* current = head_; current != NULL; current = current->next()) {
484 total++;
485 if (current->state_ == Node::WEAK) weak++;
486 if (current->state_ == Node::PENDING) pending++;
487 if (current->state_ == Node::NEAR_DEATH) near_death++;
488 if (current->state_ == Node::DESTROYED) destroyed++;
489 }
490
491 PrintF("Global Handle Statistics:\n");
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +0000492 PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000493 PrintF(" # weak = %d\n", weak);
494 PrintF(" # pending = %d\n", pending);
495 PrintF(" # near_death = %d\n", near_death);
496 PrintF(" # destroyed = %d\n", destroyed);
497 PrintF(" # total = %d\n", total);
498}
499
500void GlobalHandles::Print() {
501 PrintF("Global handles:\n");
502 for (Node* current = head_; current != NULL; current = current->next()) {
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +0000503 PrintF(" handle %p to %p (weak=%d)\n",
504 reinterpret_cast<void*>(current->handle().location()),
505 reinterpret_cast<void*>(*current->handle()),
506 current->state_ == Node::WEAK);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000507 }
508}
509
510#endif
511
ager@chromium.org8bb60582008-12-11 12:02:20 +0000512List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
513 // Lazily initialize the list to avoid startup time static constructors.
514 static List<ObjectGroup*> groups(4);
515 return &groups;
516}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000517
ager@chromium.org8bb60582008-12-11 12:02:20 +0000518void GlobalHandles::AddGroup(Object*** handles, size_t length) {
519 ObjectGroup* new_entry = new ObjectGroup(length);
520 for (size_t i = 0; i < length; ++i)
521 new_entry->objects_.Add(handles[i]);
522 ObjectGroups()->Add(new_entry);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000523}
524
525
526void GlobalHandles::RemoveObjectGroups() {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000527 List<ObjectGroup*>* object_groups = ObjectGroups();
528 for (int i = 0; i< object_groups->length(); i++) {
529 delete object_groups->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000530 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000531 object_groups->Clear();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000532}
533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534} } // namespace v8::internal