blob: 20a71499a381c02c3ab339065c451c49fed69107 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 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 "frames-inl.h"
kasperl@chromium.org061ef742009-02-27 12:16:20 +000031#include "mark-compact.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "scopeinfo.h"
33#include "string-stream.h"
34#include "top.h"
35#include "zone-inl.h"
36
37namespace v8 { namespace internal {
38
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039// Iterator that supports traversing the stack handlers of a
40// particular frame. Needs to know the top of the handler chain.
41class StackHandlerIterator BASE_EMBEDDED {
42 public:
43 StackHandlerIterator(const StackFrame* frame, StackHandler* handler)
44 : limit_(frame->fp()), handler_(handler) {
45 // Make sure the handler has already been unwound to this frame.
46 ASSERT(frame->sp() <= handler->address());
47 }
48
49 StackHandler* handler() const { return handler_; }
50
51 bool done() { return handler_->address() > limit_; }
52 void Advance() {
53 ASSERT(!done());
54 handler_ = handler_->next();
55 }
56
57 private:
58 const Address limit_;
59 StackHandler* handler_;
60};
61
62
63// -------------------------------------------------------------------------
64
65
66#define INITIALIZE_SINGLETON(type, field) field##_(this),
67StackFrameIterator::StackFrameIterator()
68 : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
kasper.lund7276f142008-07-30 08:49:36 +000069 frame_(NULL), handler_(NULL), thread_(Top::GetCurrentThread()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070 Reset();
71}
72StackFrameIterator::StackFrameIterator(ThreadLocalTop* t)
73 : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
kasper.lund7276f142008-07-30 08:49:36 +000074 frame_(NULL), handler_(NULL), thread_(t) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000075 Reset();
76}
77#undef INITIALIZE_SINGLETON
78
79
80void StackFrameIterator::Advance() {
81 ASSERT(!done());
82 // Compute the state of the calling frame before restoring
83 // callee-saved registers and unwinding handlers. This allows the
84 // frame code that computes the caller state to access the top
85 // handler and the value of any callee-saved register if needed.
86 StackFrame::State state;
87 StackFrame::Type type = frame_->GetCallerState(&state);
88
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000089 // Unwind handlers corresponding to the current frame.
90 StackHandlerIterator it(frame_, handler_);
91 while (!it.done()) it.Advance();
92 handler_ = it.handler();
93
94 // Advance to the calling frame.
95 frame_ = SingletonFor(type, &state);
96
97 // When we're done iterating over the stack frames, the handler
98 // chain must have been completely unwound.
99 ASSERT(!done() || handler_ == NULL);
100}
101
102
103void StackFrameIterator::Reset() {
kasper.lund7276f142008-07-30 08:49:36 +0000104 Address fp = Top::c_entry_fp(thread_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105 StackFrame::State state;
106 StackFrame::Type type = ExitFrame::GetStateForFramePointer(fp, &state);
107 frame_ = SingletonFor(type, &state);
kasper.lund7276f142008-07-30 08:49:36 +0000108 handler_ = StackHandler::FromAddress(Top::handler(thread_));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000109}
110
111
112StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type,
113 StackFrame::State* state) {
114#define FRAME_TYPE_CASE(type, field) \
115 case StackFrame::type: result = &field##_; break;
116
117 StackFrame* result = NULL;
118 switch (type) {
119 case StackFrame::NONE: return NULL;
120 STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
121 default: break;
122 }
123 ASSERT(result != NULL);
124 result->state_ = *state;
125 return result;
126
127#undef FRAME_TYPE_CASE
128}
129
130
131// -------------------------------------------------------------------------
132
133
134JavaScriptFrameIterator::JavaScriptFrameIterator(StackFrame::Id id) {
135 while (true) {
136 Advance();
137 if (frame()->id() == id) return;
138 }
139}
140
141
142void JavaScriptFrameIterator::Advance() {
143 do {
144 iterator_.Advance();
145 } while (!iterator_.done() && !iterator_.frame()->is_java_script());
146}
147
148
149void JavaScriptFrameIterator::AdvanceToArgumentsFrame() {
150 if (!frame()->has_adapted_arguments()) return;
151 iterator_.Advance();
152 ASSERT(iterator_.frame()->is_arguments_adaptor());
153}
154
155
156void JavaScriptFrameIterator::Reset() {
157 iterator_.Reset();
158 Advance();
159}
160
161
162// -------------------------------------------------------------------------
163
164
165void StackHandler::Cook(Code* code) {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000166 ASSERT(MarkCompactCollector::IsCompacting());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000167 ASSERT(code->contains(pc()));
168 set_pc(AddressFrom<Address>(pc() - code->instruction_start()));
169}
170
171
172void StackHandler::Uncook(Code* code) {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000173 ASSERT(MarkCompactCollector::IsCompacting());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000174 set_pc(code->instruction_start() + OffsetFrom(pc()));
175 ASSERT(code->contains(pc()));
176}
177
178
179// -------------------------------------------------------------------------
180
181
182bool StackFrame::HasHandler() const {
183 StackHandlerIterator it(this, top_handler());
184 return !it.done();
185}
186
187
188void StackFrame::CookFramesForThread(ThreadLocalTop* thread) {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000189 // Only cooking frames when the collector is compacting and thus moving code
190 // around.
191 ASSERT(MarkCompactCollector::IsCompacting());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000192 ASSERT(!thread->stack_is_cooked());
193 for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
194 it.frame()->Cook();
195 }
196 thread->set_stack_is_cooked(true);
197}
198
199
200void StackFrame::UncookFramesForThread(ThreadLocalTop* thread) {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000201 // Only uncooking frames when the collector is compacting and thus moving code
202 // around.
203 ASSERT(MarkCompactCollector::IsCompacting());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 ASSERT(thread->stack_is_cooked());
205 for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
206 it.frame()->Uncook();
207 }
208 thread->set_stack_is_cooked(false);
209}
210
211
212void StackFrame::Cook() {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000213 Code* code = this->code();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000214 for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
215 it.handler()->Cook(code);
216 }
217 ASSERT(code->contains(pc()));
218 set_pc(AddressFrom<Address>(pc() - code->instruction_start()));
219}
220
221
222void StackFrame::Uncook() {
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000223 Code* code = this->code();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000224 for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
225 it.handler()->Uncook(code);
226 }
227 set_pc(code->instruction_start() + OffsetFrom(pc()));
228 ASSERT(code->contains(pc()));
229}
230
231
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000232Code* EntryFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000233 return Heap::js_entry_code();
234}
235
236
237StackFrame::Type EntryFrame::GetCallerState(State* state) const {
238 const int offset = EntryFrameConstants::kCallerFPOffset;
239 Address fp = Memory::Address_at(this->fp() + offset);
240 return ExitFrame::GetStateForFramePointer(fp, state);
241}
242
243
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000244Code* EntryConstructFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000245 return Heap::js_construct_entry_code();
246}
247
248
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000249Code* ExitFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000250 return Heap::c_entry_code();
251}
252
253
254StackFrame::Type ExitFrame::GetCallerState(State* state) const {
255 // Setup the caller state.
256 state->sp = pp();
257 state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000258 state->pc_address
259 = reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset);
260 return ComputeType(state);
261}
262
263
264Address ExitFrame::GetCallerStackPointer() const {
265 return fp() + ExitFrameConstants::kPPDisplacement;
266}
267
268
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000269Code* ExitDebugFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270 return Heap::c_entry_debug_break_code();
271}
272
273
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274Address StandardFrame::GetExpressionAddress(int n) const {
kasper.lund7276f142008-07-30 08:49:36 +0000275 const int offset = StandardFrameConstants::kExpressionsOffset;
276 return fp() + offset - n * kPointerSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277}
278
279
280int StandardFrame::ComputeExpressionsCount() const {
281 const int offset =
282 StandardFrameConstants::kExpressionsOffset + kPointerSize;
283 Address base = fp() + offset;
284 Address limit = sp();
285 ASSERT(base >= limit); // stack grows downwards
286 // Include register-allocated locals in number of expressions.
kasper.lund7276f142008-07-30 08:49:36 +0000287 return (base - limit) / kPointerSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288}
289
290
291StackFrame::Type StandardFrame::GetCallerState(State* state) const {
292 state->sp = caller_sp();
293 state->fp = caller_fp();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 state->pc_address = reinterpret_cast<Address*>(ComputePCAddress(fp()));
295 return ComputeType(state);
296}
297
298
299bool StandardFrame::IsExpressionInsideHandler(int n) const {
300 Address address = GetExpressionAddress(n);
301 for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
302 if (it.handler()->includes(address)) return true;
303 }
304 return false;
305}
306
307
308Object* JavaScriptFrame::GetParameter(int index) const {
309 ASSERT(index >= 0 && index < ComputeParametersCount());
310 const int offset = JavaScriptFrameConstants::kParam0Offset;
311 return Memory::Object_at(pp() + offset - (index * kPointerSize));
312}
313
314
315int JavaScriptFrame::ComputeParametersCount() const {
316 Address base = pp() + JavaScriptFrameConstants::kReceiverOffset;
317 Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset;
kasper.lund7276f142008-07-30 08:49:36 +0000318 return (base - limit) / kPointerSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319}
320
321
322bool JavaScriptFrame::IsConstructor() const {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000323 Address fp = caller_fp();
324 if (has_adapted_arguments()) {
325 // Skip the arguments adaptor frame and look at the real caller.
326 fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
327 }
328 return IsConstructFrame(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000329}
330
331
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000332Code* JavaScriptFrame::code() const {
333 JSFunction* function = JSFunction::cast(this->function());
334 return function->shared()->code();
335}
336
337
338Code* ArgumentsAdaptorFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000339 return Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline);
340}
341
342
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000343Code* InternalFrame::code() const {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000344 const int offset = InternalFrameConstants::kCodeOffset;
345 Object* code = Memory::Object_at(fp() + offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346 ASSERT(code != NULL);
347 return Code::cast(code);
348}
349
350
351void StackFrame::PrintIndex(StringStream* accumulator,
352 PrintMode mode,
353 int index) {
354 accumulator->Add((mode == OVERVIEW) ? "%5d: " : "[%d]: ", index);
355}
356
357
358void JavaScriptFrame::Print(StringStream* accumulator,
359 PrintMode mode,
360 int index) const {
361 HandleScope scope;
362 Object* receiver = this->receiver();
363 Object* function = this->function();
364
365 accumulator->PrintSecurityTokenIfChanged(function);
366 PrintIndex(accumulator, mode, index);
367 Code* code = NULL;
368 if (IsConstructor()) accumulator->Add("new ");
369 accumulator->PrintFunction(function, receiver, &code);
370 accumulator->Add("(this=%o", receiver);
371
372 // Get scope information for nicer output, if possible. If code is
373 // NULL, or doesn't contain scope info, info will return 0 for the
374 // number of parameters, stack slots, or context slots.
375 ScopeInfo<PreallocatedStorage> info(code);
376
377 // Print the parameters.
378 int parameters_count = ComputeParametersCount();
379 for (int i = 0; i < parameters_count; i++) {
380 accumulator->Add(",");
381 // If we have a name for the parameter we print it. Nameless
382 // parameters are either because we have more actual parameters
383 // than formal parameters or because we have no scope information.
384 if (i < info.number_of_parameters()) {
385 accumulator->PrintName(*info.parameter_name(i));
386 accumulator->Add("=");
387 }
388 accumulator->Add("%o", GetParameter(i));
389 }
390
391 accumulator->Add(")");
392 if (mode == OVERVIEW) {
393 accumulator->Add("\n");
394 return;
395 }
396 accumulator->Add(" {\n");
397
398 // Compute the number of locals and expression stack elements.
399 int stack_locals_count = info.number_of_stack_slots();
400 int heap_locals_count = info.number_of_context_slots();
401 int expressions_count = ComputeExpressionsCount();
402
403 // Print stack-allocated local variables.
404 if (stack_locals_count > 0) {
405 accumulator->Add(" // stack-allocated locals\n");
406 }
407 for (int i = 0; i < stack_locals_count; i++) {
408 accumulator->Add(" var ");
409 accumulator->PrintName(*info.stack_slot_name(i));
410 accumulator->Add(" = ");
411 if (i < expressions_count) {
412 accumulator->Add("%o", GetExpression(i));
413 } else {
414 accumulator->Add("// no expression found - inconsistent frame?");
415 }
416 accumulator->Add("\n");
417 }
418
419 // Try to get hold of the context of this frame.
420 Context* context = NULL;
421 if (this->context() != NULL && this->context()->IsContext()) {
422 context = Context::cast(this->context());
423 }
424
425 // Print heap-allocated local variables.
426 if (heap_locals_count > Context::MIN_CONTEXT_SLOTS) {
427 accumulator->Add(" // heap-allocated locals\n");
428 }
429 for (int i = Context::MIN_CONTEXT_SLOTS; i < heap_locals_count; i++) {
430 accumulator->Add(" var ");
431 accumulator->PrintName(*info.context_slot_name(i));
432 accumulator->Add(" = ");
433 if (context != NULL) {
434 if (i < context->length()) {
435 accumulator->Add("%o", context->get(i));
436 } else {
437 accumulator->Add(
438 "// warning: missing context slot - inconsistent frame?");
439 }
440 } else {
441 accumulator->Add("// warning: no context found - inconsistent frame?");
442 }
443 accumulator->Add("\n");
444 }
445
446 // Print the expression stack.
kasper.lund7276f142008-07-30 08:49:36 +0000447 int expressions_start = stack_locals_count;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000448 if (expressions_start < expressions_count) {
449 accumulator->Add(" // expression stack (top to bottom)\n");
450 }
451 for (int i = expressions_count - 1; i >= expressions_start; i--) {
452 if (IsExpressionInsideHandler(i)) continue;
453 accumulator->Add(" [%02d] : %o\n", i, GetExpression(i));
454 }
455
456 // Print details about the function.
457 if (FLAG_max_stack_trace_source_length != 0 && code != NULL) {
458 SharedFunctionInfo* shared = JSFunction::cast(function)->shared();
459 accumulator->Add("--------- s o u r c e c o d e ---------\n");
460 shared->SourceCodePrint(accumulator, FLAG_max_stack_trace_source_length);
461 accumulator->Add("\n-----------------------------------------\n");
462 }
463
464 accumulator->Add("}\n\n");
465}
466
467
468void ArgumentsAdaptorFrame::Print(StringStream* accumulator,
469 PrintMode mode,
470 int index) const {
471 int actual = ComputeParametersCount();
472 int expected = -1;
473 Object* function = this->function();
474 if (function->IsJSFunction()) {
475 expected = JSFunction::cast(function)->shared()->formal_parameter_count();
476 }
477
478 PrintIndex(accumulator, mode, index);
479 accumulator->Add("arguments adaptor frame: %d->%d", actual, expected);
480 if (mode == OVERVIEW) {
481 accumulator->Add("\n");
482 return;
483 }
484 accumulator->Add(" {\n");
485
486 // Print actual arguments.
487 if (actual > 0) accumulator->Add(" // actual arguments\n");
488 for (int i = 0; i < actual; i++) {
489 accumulator->Add(" [%02d] : %o", i, GetParameter(i));
490 if (expected != -1 && i >= expected) {
491 accumulator->Add(" // not passed to callee");
492 }
493 accumulator->Add("\n");
494 }
495
496 accumulator->Add("}\n\n");
497}
498
499
500void EntryFrame::Iterate(ObjectVisitor* v) const {
501 StackHandlerIterator it(this, top_handler());
502 ASSERT(!it.done());
503 StackHandler* handler = it.handler();
504 ASSERT(handler->is_entry());
505 handler->Iterate(v);
506 // Make sure that there's the entry frame does not contain more than
507 // one stack handler.
508 if (kDebug) {
509 it.Advance();
510 ASSERT(it.done());
511 }
512}
513
514
515void StandardFrame::IterateExpressions(ObjectVisitor* v) const {
516 const int offset = StandardFrameConstants::kContextOffset;
517 Object** base = &Memory::Object_at(sp());
518 Object** limit = &Memory::Object_at(fp() + offset) + 1;
519 for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
520 StackHandler* handler = it.handler();
521 // Traverse pointers down to - but not including - the next
522 // handler in the handler chain. Update the base to skip the
523 // handler and allow the handler to traverse its own pointers.
524 const Address address = handler->address();
525 v->VisitPointers(base, reinterpret_cast<Object**>(address));
526 base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize);
527 // Traverse the pointers in the handler itself.
528 handler->Iterate(v);
529 }
530 v->VisitPointers(base, limit);
531}
532
533
534void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
535 IterateExpressions(v);
536
537 // Traverse callee-saved registers, receiver, and parameters.
538 const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset;
539 const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset;
540 Object** base = &Memory::Object_at(fp() + kBaseOffset);
541 Object** limit = &Memory::Object_at(pp() + kLimitOffset) + 1;
542 v->VisitPointers(base, limit);
543}
544
545
546void InternalFrame::Iterate(ObjectVisitor* v) const {
547 // Internal frames only have object pointers on the expression stack
548 // as they never have any arguments.
549 IterateExpressions(v);
550}
551
552
553// -------------------------------------------------------------------------
554
555
556JavaScriptFrame* StackFrameLocator::FindJavaScriptFrame(int n) {
557 ASSERT(n >= 0);
558 for (int i = 0; i <= n; i++) {
559 while (!iterator_.frame()->is_java_script()) iterator_.Advance();
560 if (i == n) return JavaScriptFrame::cast(iterator_.frame());
561 iterator_.Advance();
562 }
563 UNREACHABLE();
564 return NULL;
565}
566
567
568// -------------------------------------------------------------------------
569
570
571int NumRegs(RegList reglist) {
572 int n = 0;
573 while (reglist != 0) {
574 n++;
575 reglist &= reglist - 1; // clear one bit
576 }
577 return n;
578}
579
580
581int JSCallerSavedCode(int n) {
582 static int reg_code[kNumJSCallerSaved];
583 static bool initialized = false;
584 if (!initialized) {
585 initialized = true;
586 int i = 0;
587 for (int r = 0; r < kNumRegs; r++)
588 if ((kJSCallerSaved & (1 << r)) != 0)
589 reg_code[i++] = r;
590
591 ASSERT(i == kNumJSCallerSaved);
592 }
593 ASSERT(0 <= n && n < kNumJSCallerSaved);
594 return reg_code[n];
595}
596
597
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000598} } // namespace v8::internal