1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 2 // 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 <stdlib.h> 29 30 #include "v8.h" 31 32 #include "api.h" 33 #include "bootstrapper.h" 34 #include "codegen-inl.h" 35 #include "debug.h" 36 #include "simulator.h" 37 #include "v8threads.h" 38 39 namespace v8 { 40 namespace internal { 41 42 43 static Handle<Object> Invoke(bool construct, 44 Handle<JSFunction> func, 45 Handle<Object> receiver, 46 int argc, 47 Object*** args, 48 bool* has_pending_exception) { 49 // Make sure we have a real function, not a boilerplate function. 50 ASSERT(!func->IsBoilerplate()); 51 52 // Entering JavaScript. 53 VMState state(JS); 54 55 // Placeholder for return value. 56 Object* value = reinterpret_cast<Object*>(kZapValue); 57 58 typedef Object* (*JSEntryFunction)( 59 byte* entry, 60 Object* function, 61 Object* receiver, 62 int argc, 63 Object*** args); 64 65 Handle<Code> code; 66 if (construct) { 67 JSConstructEntryStub stub; 68 code = stub.GetCode(); 69 } else { 70 JSEntryStub stub; 71 code = stub.GetCode(); 72 } 73 74 // Convert calls on global objects to be calls on the global 75 // receiver instead to avoid having a 'this' pointer which refers 76 // directly to a global object. 77 if (receiver->IsGlobalObject()) { 78 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver); 79 receiver = Handle<JSObject>(global->global_receiver()); 80 } 81 82 // Make sure that the global object of the context we're about to 83 // make the current one is indeed a global object. 84 ASSERT(func->context()->global()->IsGlobalObject()); 85 86 { 87 // Save and restore context around invocation and block the 88 // allocation of handles without explicit handle scopes. 89 SaveContext save; 90 NoHandleAllocation na; 91 JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry()); 92 93 // Call the function through the right JS entry stub. 94 byte* entry_address = func->code()->entry(); 95 JSFunction* function = *func; 96 Object* receiver_pointer = *receiver; 97 value = CALL_GENERATED_CODE(entry, entry_address, function, 98 receiver_pointer, argc, args); 99 } 100 101 #ifdef DEBUG 102 value->Verify(); 103 #endif 104 105 // Update the pending exception flag and return the value. 106 *has_pending_exception = value->IsException(); 107 ASSERT(*has_pending_exception == Top::has_pending_exception()); 108 if (*has_pending_exception) { 109 Top::ReportPendingMessages(); 110 return Handle<Object>(); 111 } else { 112 Top::clear_pending_message(); 113 } 114 115 return Handle<Object>(value); 116 } 117 118 119 Handle<Object> Execution::Call(Handle<JSFunction> func, 120 Handle<Object> receiver, 121 int argc, 122 Object*** args, 123 bool* pending_exception) { 124 return Invoke(false, func, receiver, argc, args, pending_exception); 125 } 126 127 128 Handle<Object> Execution::New(Handle<JSFunction> func, int argc, 129 Object*** args, bool* pending_exception) { 130 return Invoke(true, func, Top::global(), argc, args, pending_exception); 131 } 132 133 134 Handle<Object> Execution::TryCall(Handle<JSFunction> func, 135 Handle<Object> receiver, 136 int argc, 137 Object*** args, 138 bool* caught_exception) { 139 // Enter a try-block while executing the JavaScript code. To avoid 140 // duplicate error printing it must be non-verbose. Also, to avoid 141 // creating message objects during stack overflow we shouldn't 142 // capture messages. 143 v8::TryCatch catcher; 144 catcher.SetVerbose(false); 145 catcher.SetCaptureMessage(false); 146 147 Handle<Object> result = Invoke(false, func, receiver, argc, args, 148 caught_exception); 149 150 if (*caught_exception) { 151 ASSERT(catcher.HasCaught()); 152 ASSERT(Top::has_pending_exception()); 153 ASSERT(Top::external_caught_exception()); 154 if (Top::pending_exception() == Heap::termination_exception()) { 155 result = Factory::termination_exception(); 156 } else { 157 result = v8::Utils::OpenHandle(*catcher.Exception()); 158 } 159 Top::OptionalRescheduleException(true); 160 } 161 162 ASSERT(!Top::has_pending_exception()); 163 ASSERT(!Top::external_caught_exception()); 164 return result; 165 } 166 167 168 Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) { 169 ASSERT(!object->IsJSFunction()); 170 171 // If you return a function from here, it will be called when an 172 // attempt is made to call the given object as a function. 173 174 // Regular expressions can be called as functions in both Firefox 175 // and Safari so we allow it too. 176 if (object->IsJSRegExp()) { 177 Handle<String> exec = Factory::exec_symbol(); 178 return Handle<Object>(object->GetProperty(*exec)); 179 } 180 181 // Objects created through the API can have an instance-call handler 182 // that should be used when calling the object as a function. 183 if (object->IsHeapObject() && 184 HeapObject::cast(*object)->map()->has_instance_call_handler()) { 185 return Handle<JSFunction>( 186 Top::global_context()->call_as_function_delegate()); 187 } 188 189 return Factory::undefined_value(); 190 } 191 192 193 Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { 194 ASSERT(!object->IsJSFunction()); 195 196 // If you return a function from here, it will be called when an 197 // attempt is made to call the given object as a constructor. 198 199 // Objects created through the API can have an instance-call handler 200 // that should be used when calling the object as a function. 201 if (object->IsHeapObject() && 202 HeapObject::cast(*object)->map()->has_instance_call_handler()) { 203 return Handle<JSFunction>( 204 Top::global_context()->call_as_constructor_delegate()); 205 } 206 207 return Factory::undefined_value(); 208 } 209 210 211 // Static state for stack guards. 212 StackGuard::ThreadLocal StackGuard::thread_local_; 213 214 215 bool StackGuard::IsStackOverflow() { 216 ExecutionAccess access; 217 return (thread_local_.jslimit_ != kInterruptLimit && 218 thread_local_.climit_ != kInterruptLimit); 219 } 220 221 222 void StackGuard::EnableInterrupts() { 223 ExecutionAccess access; 224 if (IsSet(access)) { 225 set_limits(kInterruptLimit, access); 226 } 227 } 228 229 230 void StackGuard::SetStackLimit(uintptr_t limit) { 231 ExecutionAccess access; 232 // If the current limits are special (eg due to a pending interrupt) then 233 // leave them alone. 234 uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(limit); 235 if (thread_local_.jslimit_ == thread_local_.real_jslimit_) { 236 thread_local_.jslimit_ = jslimit; 237 } 238 if (thread_local_.climit_ == thread_local_.real_climit_) { 239 thread_local_.climit_ = limit; 240 } 241 thread_local_.real_climit_ = limit; 242 thread_local_.real_jslimit_ = jslimit; 243 } 244 245 246 void StackGuard::DisableInterrupts() { 247 ExecutionAccess access; 248 reset_limits(access); 249 } 250 251 252 bool StackGuard::IsSet(const ExecutionAccess& lock) { 253 return thread_local_.interrupt_flags_ != 0; 254 } 255 256 257 bool StackGuard::IsInterrupted() { 258 ExecutionAccess access; 259 return thread_local_.interrupt_flags_ & INTERRUPT; 260 } 261 262 263 void StackGuard::Interrupt() { 264 ExecutionAccess access; 265 thread_local_.interrupt_flags_ |= INTERRUPT; 266 set_limits(kInterruptLimit, access); 267 } 268 269 270 bool StackGuard::IsPreempted() { 271 ExecutionAccess access; 272 return thread_local_.interrupt_flags_ & PREEMPT; 273 } 274 275 276 void StackGuard::Preempt() { 277 ExecutionAccess access; 278 thread_local_.interrupt_flags_ |= PREEMPT; 279 set_limits(kInterruptLimit, access); 280 } 281 282 283 bool StackGuard::IsTerminateExecution() { 284 ExecutionAccess access; 285 return thread_local_.interrupt_flags_ & TERMINATE; 286 } 287 288 289 void StackGuard::TerminateExecution() { 290 ExecutionAccess access; 291 thread_local_.interrupt_flags_ |= TERMINATE; 292 set_limits(kInterruptLimit, access); 293 } 294 295 296 #ifdef ENABLE_DEBUGGER_SUPPORT 297 bool StackGuard::IsDebugBreak() { 298 ExecutionAccess access; 299 return thread_local_.interrupt_flags_ & DEBUGBREAK; 300 } 301 302 303 void StackGuard::DebugBreak() { 304 ExecutionAccess access; 305 thread_local_.interrupt_flags_ |= DEBUGBREAK; 306 set_limits(kInterruptLimit, access); 307 } 308 309 310 bool StackGuard::IsDebugCommand() { 311 ExecutionAccess access; 312 return thread_local_.interrupt_flags_ & DEBUGCOMMAND; 313 } 314 315 316 void StackGuard::DebugCommand() { 317 if (FLAG_debugger_auto_break) { 318 ExecutionAccess access; 319 thread_local_.interrupt_flags_ |= DEBUGCOMMAND; 320 set_limits(kInterruptLimit, access); 321 } 322 } 323 #endif 324 325 void StackGuard::Continue(InterruptFlag after_what) { 326 ExecutionAccess access; 327 thread_local_.interrupt_flags_ &= ~static_cast<int>(after_what); 328 if (thread_local_.interrupt_flags_ == 0) { 329 reset_limits(access); 330 } 331 } 332 333 334 int StackGuard::ArchiveSpacePerThread() { 335 return sizeof(ThreadLocal); 336 } 337 338 339 char* StackGuard::ArchiveStackGuard(char* to) { 340 ExecutionAccess access; 341 memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal)); 342 ThreadLocal blank; 343 thread_local_ = blank; 344 return to + sizeof(ThreadLocal); 345 } 346 347 348 char* StackGuard::RestoreStackGuard(char* from) { 349 ExecutionAccess access; 350 memcpy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal)); 351 Heap::SetStackLimits(); 352 return from + sizeof(ThreadLocal); 353 } 354 355 356 static internal::Thread::LocalStorageKey stack_limit_key = 357 internal::Thread::CreateThreadLocalKey(); 358 359 360 void StackGuard::FreeThreadResources() { 361 Thread::SetThreadLocal( 362 stack_limit_key, 363 reinterpret_cast<void*>(thread_local_.real_climit_)); 364 } 365 366 367 void StackGuard::ThreadLocal::Clear() { 368 real_jslimit_ = kIllegalLimit; 369 jslimit_ = kIllegalLimit; 370 real_climit_ = kIllegalLimit; 371 climit_ = kIllegalLimit; 372 nesting_ = 0; 373 postpone_interrupts_nesting_ = 0; 374 interrupt_flags_ = 0; 375 Heap::SetStackLimits(); 376 } 377 378 379 void StackGuard::ThreadLocal::Initialize() { 380 if (real_climit_ == kIllegalLimit) { 381 // Takes the address of the limit variable in order to find out where 382 // the top of stack is right now. 383 uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize; 384 ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize); 385 real_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit); 386 jslimit_ = SimulatorStack::JsLimitFromCLimit(limit); 387 real_climit_ = limit; 388 climit_ = limit; 389 Heap::SetStackLimits(); 390 } 391 nesting_ = 0; 392 postpone_interrupts_nesting_ = 0; 393 interrupt_flags_ = 0; 394 } 395 396 397 void StackGuard::ClearThread(const ExecutionAccess& lock) { 398 thread_local_.Clear(); 399 } 400 401 402 void StackGuard::InitThread(const ExecutionAccess& lock) { 403 thread_local_.Initialize(); 404 void* stored_limit = Thread::GetThreadLocal(stack_limit_key); 405 // You should hold the ExecutionAccess lock when you call this. 406 if (stored_limit != NULL) { 407 StackGuard::SetStackLimit(reinterpret_cast<intptr_t>(stored_limit)); 408 } 409 } 410 411 412 // --- C a l l s t o n a t i v e s --- 413 414 #define RETURN_NATIVE_CALL(name, argc, argv, has_pending_exception) \ 415 do { \ 416 Object** args[argc] = argv; \ 417 ASSERT(has_pending_exception != NULL); \ 418 return Call(Top::name##_fun(), Top::builtins(), argc, args, \ 419 has_pending_exception); \ 420 } while (false) 421 422 423 Handle<Object> Execution::ToBoolean(Handle<Object> obj) { 424 // See the similar code in runtime.js:ToBoolean. 425 if (obj->IsBoolean()) return obj; 426 bool result = true; 427 if (obj->IsString()) { 428 result = Handle<String>::cast(obj)->length() != 0; 429 } else if (obj->IsNull() || obj->IsUndefined()) { 430 result = false; 431 } else if (obj->IsNumber()) { 432 double value = obj->Number(); 433 result = !((value == 0) || isnan(value)); 434 } 435 return Handle<Object>(Heap::ToBoolean(result)); 436 } 437 438 439 Handle<Object> Execution::ToNumber(Handle<Object> obj, bool* exc) { 440 RETURN_NATIVE_CALL(to_number, 1, { obj.location() }, exc); 441 } 442 443 444 Handle<Object> Execution::ToString(Handle<Object> obj, bool* exc) { 445 RETURN_NATIVE_CALL(to_string, 1, { obj.location() }, exc); 446 } 447 448 449 Handle<Object> Execution::ToDetailString(Handle<Object> obj, bool* exc) { 450 RETURN_NATIVE_CALL(to_detail_string, 1, { obj.location() }, exc); 451 } 452 453 454 Handle<Object> Execution::ToObject(Handle<Object> obj, bool* exc) { 455 if (obj->IsJSObject()) return obj; 456 RETURN_NATIVE_CALL(to_object, 1, { obj.location() }, exc); 457 } 458 459 460 Handle<Object> Execution::ToInteger(Handle<Object> obj, bool* exc) { 461 RETURN_NATIVE_CALL(to_integer, 1, { obj.location() }, exc); 462 } 463 464 465 Handle<Object> Execution::ToUint32(Handle<Object> obj, bool* exc) { 466 RETURN_NATIVE_CALL(to_uint32, 1, { obj.location() }, exc); 467 } 468 469 470 Handle<Object> Execution::ToInt32(Handle<Object> obj, bool* exc) { 471 RETURN_NATIVE_CALL(to_int32, 1, { obj.location() }, exc); 472 } 473 474 475 Handle<Object> Execution::NewDate(double time, bool* exc) { 476 Handle<Object> time_obj = Factory::NewNumber(time); 477 RETURN_NATIVE_CALL(create_date, 1, { time_obj.location() }, exc); 478 } 479 480 481 #undef RETURN_NATIVE_CALL 482 483 484 Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) { 485 int int_index = static_cast<int>(index); 486 if (int_index < 0 || int_index >= string->length()) { 487 return Factory::undefined_value(); 488 } 489 490 Handle<Object> char_at = 491 GetProperty(Top::builtins(), Factory::char_at_symbol()); 492 if (!char_at->IsJSFunction()) { 493 return Factory::undefined_value(); 494 } 495 496 bool caught_exception; 497 Handle<Object> index_object = Factory::NewNumberFromInt(int_index); 498 Object** index_arg[] = { index_object.location() }; 499 Handle<Object> result = TryCall(Handle<JSFunction>::cast(char_at), 500 string, 501 ARRAY_SIZE(index_arg), 502 index_arg, 503 &caught_exception); 504 if (caught_exception) { 505 return Factory::undefined_value(); 506 } 507 return result; 508 } 509 510 511 Handle<JSFunction> Execution::InstantiateFunction( 512 Handle<FunctionTemplateInfo> data, bool* exc) { 513 // Fast case: see if the function has already been instantiated 514 int serial_number = Smi::cast(data->serial_number())->value(); 515 Object* elm = 516 Top::global_context()->function_cache()->GetElement(serial_number); 517 if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm)); 518 // The function has not yet been instantiated in this context; do it. 519 Object** args[1] = { Handle<Object>::cast(data).location() }; 520 Handle<Object> result = 521 Call(Top::instantiate_fun(), Top::builtins(), 1, args, exc); 522 if (*exc) return Handle<JSFunction>::null(); 523 return Handle<JSFunction>::cast(result); 524 } 525 526 527 Handle<JSObject> Execution::InstantiateObject(Handle<ObjectTemplateInfo> data, 528 bool* exc) { 529 if (data->property_list()->IsUndefined() && 530 !data->constructor()->IsUndefined()) { 531 // Initialization to make gcc happy. 532 Object* result = NULL; 533 { 534 HandleScope scope; 535 Handle<FunctionTemplateInfo> cons_template = 536 Handle<FunctionTemplateInfo>( 537 FunctionTemplateInfo::cast(data->constructor())); 538 Handle<JSFunction> cons = InstantiateFunction(cons_template, exc); 539 if (*exc) return Handle<JSObject>::null(); 540 Handle<Object> value = New(cons, 0, NULL, exc); 541 if (*exc) return Handle<JSObject>::null(); 542 result = *value; 543 } 544 ASSERT(!*exc); 545 return Handle<JSObject>(JSObject::cast(result)); 546 } else { 547 Object** args[1] = { Handle<Object>::cast(data).location() }; 548 Handle<Object> result = 549 Call(Top::instantiate_fun(), Top::builtins(), 1, args, exc); 550 if (*exc) return Handle<JSObject>::null(); 551 return Handle<JSObject>::cast(result); 552 } 553 } 554 555 556 void Execution::ConfigureInstance(Handle<Object> instance, 557 Handle<Object> instance_template, 558 bool* exc) { 559 Object** args[2] = { instance.location(), instance_template.location() }; 560 Execution::Call(Top::configure_instance_fun(), Top::builtins(), 2, args, exc); 561 } 562 563 564 Handle<String> Execution::GetStackTraceLine(Handle<Object> recv, 565 Handle<JSFunction> fun, 566 Handle<Object> pos, 567 Handle<Object> is_global) { 568 const int argc = 4; 569 Object** args[argc] = { recv.location(), 570 Handle<Object>::cast(fun).location(), 571 pos.location(), 572 is_global.location() }; 573 bool caught_exception = false; 574 Handle<Object> result = TryCall(Top::get_stack_trace_line_fun(), 575 Top::builtins(), argc, args, 576 &caught_exception); 577 if (caught_exception || !result->IsString()) return Factory::empty_symbol(); 578 return Handle<String>::cast(result); 579 } 580 581 582 static Object* RuntimePreempt() { 583 // Clear the preempt request flag. 584 StackGuard::Continue(PREEMPT); 585 586 ContextSwitcher::PreemptionReceived(); 587 588 #ifdef ENABLE_DEBUGGER_SUPPORT 589 if (Debug::InDebugger()) { 590 // If currently in the debugger don't do any actual preemption but record 591 // that preemption occoured while in the debugger. 592 Debug::PreemptionWhileInDebugger(); 593 } else { 594 // Perform preemption. 595 v8::Unlocker unlocker; 596 Thread::YieldCPU(); 597 } 598 #else 599 // Perform preemption. 600 v8::Unlocker unlocker; 601 Thread::YieldCPU(); 602 #endif 603 604 return Heap::undefined_value(); 605 } 606 607 608 #ifdef ENABLE_DEBUGGER_SUPPORT 609 Object* Execution::DebugBreakHelper() { 610 // Just continue if breaks are disabled. 611 if (Debug::disable_break()) { 612 return Heap::undefined_value(); 613 } 614 615 // Ignore debug break during bootstrapping. 616 if (Bootstrapper::IsActive()) { 617 return Heap::undefined_value(); 618 } 619 620 { 621 JavaScriptFrameIterator it; 622 ASSERT(!it.done()); 623 Object* fun = it.frame()->function(); 624 if (fun && fun->IsJSFunction()) { 625 // Don't stop in builtin functions. 626 if (JSFunction::cast(fun)->IsBuiltin()) { 627 return Heap::undefined_value(); 628 } 629 GlobalObject* global = JSFunction::cast(fun)->context()->global(); 630 // Don't stop in debugger functions. 631 if (Debug::IsDebugGlobal(global)) { 632 return Heap::undefined_value(); 633 } 634 } 635 } 636 637 // Collect the break state before clearing the flags. 638 bool debug_command_only = 639 StackGuard::IsDebugCommand() && !StackGuard::IsDebugBreak(); 640 641 // Clear the debug break request flag. 642 StackGuard::Continue(DEBUGBREAK); 643 644 ProcessDebugMesssages(debug_command_only); 645 646 // Return to continue execution. 647 return Heap::undefined_value(); 648 } 649 650 void Execution::ProcessDebugMesssages(bool debug_command_only) { 651 // Clear the debug command request flag. 652 StackGuard::Continue(DEBUGCOMMAND); 653 654 HandleScope scope; 655 // Enter the debugger. Just continue if we fail to enter the debugger. 656 EnterDebugger debugger; 657 if (debugger.FailedToEnter()) { 658 return; 659 } 660 661 // Notify the debug event listeners. Indicate auto continue if the break was 662 // a debug command break. 663 Debugger::OnDebugBreak(Factory::undefined_value(), debug_command_only); 664 } 665 666 667 #endif 668 669 Object* Execution::HandleStackGuardInterrupt() { 670 #ifdef ENABLE_DEBUGGER_SUPPORT 671 if (StackGuard::IsDebugBreak() || StackGuard::IsDebugCommand()) { 672 DebugBreakHelper(); 673 } 674 #endif 675 if (StackGuard::IsPreempted()) RuntimePreempt(); 676 if (StackGuard::IsTerminateExecution()) { 677 StackGuard::Continue(TERMINATE); 678 return Top::TerminateExecution(); 679 } 680 if (StackGuard::IsInterrupted()) { 681 // interrupt 682 StackGuard::Continue(INTERRUPT); 683 return Top::StackOverflow(); 684 } 685 return Heap::undefined_value(); 686 } 687 688 // --- G C E x t e n s i o n --- 689 690 const char* GCExtension::kSource = "native function gc();"; 691 692 693 v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( 694 v8::Handle<v8::String> str) { 695 return v8::FunctionTemplate::New(GCExtension::GC); 696 } 697 698 699 v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { 700 // All allocation spaces other than NEW_SPACE have the same effect. 701 Heap::CollectAllGarbage(false); 702 return v8::Undefined(); 703 } 704 705 706 static GCExtension kGCExtension; 707 v8::DeclareExtension kGCExtensionDeclaration(&kGCExtension); 708 709 } } // namespace v8::internal 710