From: christian.plesner.hansen@gmail.com Date: Wed, 4 Nov 2009 08:51:48 +0000 (+0000) Subject: Reverting 3174. Aka reapplying 3150, 3151 and 3159. Aka api accessor X-Git-Tag: upstream/4.7.83~23017 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a1b2f47600e4065f94148cf85b7c6f3e431439db;p=platform%2Fupstream%2Fv8.git Reverting 3174. Aka reapplying 3150, 3151 and 3159. Aka api accessor ics. Review URL: http://codereview.chromium.org/341082 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3209 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/api.h b/src/api.h index 1221f352c..a28e1f077 100644 --- a/src/api.h +++ b/src/api.h @@ -125,6 +125,15 @@ static inline v8::internal::Handle FromCData(T obj) { } +class ApiFunction { + public: + explicit ApiFunction(v8::internal::Address addr) : addr_(addr) { } + v8::internal::Address address() { return addr_; } + private: + v8::internal::Address addr_; +}; + + v8::Arguments::Arguments(v8::Local data, v8::Local holder, v8::Local callee, diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 178536423..af13ccd44 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -5777,7 +5777,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, Label* throw_out_of_memory_exception, - StackFrame::Type frame_type, + ExitFrame::Mode mode, bool do_gc, bool always_allocate) { // r0: result parameter for PerformGC, if any @@ -5837,7 +5837,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // r0:r1: result // sp: stack pointer // fp: frame pointer - __ LeaveExitFrame(frame_type); + __ LeaveExitFrame(mode); // check if we should retry or throw exception Label retry; @@ -5883,12 +5883,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // this by performing a garbage collection and retrying the // builtin once. - StackFrame::Type frame_type = is_debug_break - ? StackFrame::EXIT_DEBUG - : StackFrame::EXIT; + ExitFrame::Mode mode = is_debug_break + ? ExitFrame::MODE_DEBUG + : ExitFrame::MODE_NORMAL; // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(frame_type); + __ EnterExitFrame(mode); // r4: number of arguments (C callee-saved) // r5: pointer to builtin function (C callee-saved) @@ -5903,7 +5903,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, false, false); @@ -5912,7 +5912,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, false); @@ -5923,7 +5923,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, true); diff --git a/src/arm/frames-arm.cc b/src/arm/frames-arm.cc index 6fde4b73c..b0fa13a5a 100644 --- a/src/arm/frames-arm.cc +++ b/src/arm/frames-arm.cc @@ -54,23 +54,24 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { if (fp == 0) return NONE; // Compute frame type and stack pointer. Address sp = fp + ExitFrameConstants::kSPDisplacement; - Type type; - if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) { - type = EXIT_DEBUG; + const int offset = ExitFrameConstants::kCodeOffset; + Object* code = Memory::Object_at(fp + offset); + bool is_debug_exit = code->IsSmi(); + if (is_debug_exit) { sp -= kNumJSCallerSaved * kPointerSize; - } else { - type = EXIT; } // Fill in the state. state->sp = sp; state->fp = fp; state->pc_address = reinterpret_cast(sp - 1 * kPointerSize); - return type; + return EXIT; } void ExitFrame::Iterate(ObjectVisitor* v) const { - // Do nothing + v->VisitPointer(&code_slot()); + // The arguments are traversed as part of the expression stack of + // the calling frame. } diff --git a/src/arm/frames-arm.h b/src/arm/frames-arm.h index 0874c0927..4924c1aeb 100644 --- a/src/arm/frames-arm.h +++ b/src/arm/frames-arm.h @@ -100,7 +100,7 @@ class ExitFrameConstants : public AllStatic { static const int kSPDisplacement = -1 * kPointerSize; // The debug marker is just above the frame pointer. - static const int kDebugMarkOffset = -1 * kPointerSize; + static const int kCodeOffset = -1 * kPointerSize; static const int kSavedRegistersOffset = 0 * kPointerSize; diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 45c6540ee..dc73bad93 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -274,9 +274,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { } -void MacroAssembler::EnterExitFrame(StackFrame::Type type) { - ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); - +void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) { // Compute the argv pointer and keep it in a callee-saved register. // r0 is argc. add(r6, sp, Operand(r0, LSL, kPointerSizeLog2)); @@ -298,8 +296,11 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { stm(db_w, sp, fp.bit() | ip.bit() | lr.bit()); mov(fp, Operand(sp)); // setup new frame pointer - // Push debug marker. - mov(ip, Operand(type == StackFrame::EXIT_DEBUG ? 1 : 0)); + if (mode == ExitFrame::MODE_DEBUG) { + mov(ip, Operand(Smi::FromInt(0))); + } else { + mov(ip, Operand(CodeObject())); + } push(ip); // Save the frame pointer and the context in top. @@ -316,7 +317,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { #ifdef ENABLE_DEBUGGER_SUPPORT // Save the state of all registers to the stack from the memory // location. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // Use sp as base to push. CopyRegistersFromMemoryToStack(sp, kJSCallerSaved); } @@ -348,14 +349,14 @@ void MacroAssembler::AlignStack(int offset) { } -void MacroAssembler::LeaveExitFrame(StackFrame::Type type) { +void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) { #ifdef ENABLE_DEBUGGER_SUPPORT // Restore the memory copy of the registers by digging them out from // the stack. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // This code intentionally clobbers r2 and r3. const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize; - const int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize; + const int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize; add(r3, fp, Operand(kOffset)); CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index e37bb5e1c..6dc2b7ae8 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -87,14 +87,14 @@ class MacroAssembler: public Assembler { void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter specific kind of exit frame; either EXIT or - // EXIT_DEBUG. Expects the number of arguments in register r0 and + // Enter specific kind of exit frame; either normal or debug mode. + // Expects the number of arguments in register r0 and // the builtin function to call in register r1. Exits with argc in // r4, argv in r6, and and the builtin function to call in r5. - void EnterExitFrame(StackFrame::Type type); + void EnterExitFrame(ExitFrame::Mode mode); // Leave the current exit frame. Expects the return value in r0. - void LeaveExitFrame(StackFrame::Type type); + void LeaveExitFrame(ExitFrame::Mode mode); // Align the stack by optionally pushing a Smi zero. void AlignStack(int offset); diff --git a/src/assembler.cc b/src/assembler.cc index 34595f83f..34346a910 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -522,6 +522,10 @@ ExternalReference::ExternalReference(Builtins::CFunctionId id) : address_(Redirect(Builtins::c_function_address(id))) {} +ExternalReference::ExternalReference(ApiFunction* fun) + : address_(Redirect(fun->address())) {} + + ExternalReference::ExternalReference(Builtins::Name name) : address_(Builtins::builtin_address(name)) {} @@ -608,6 +612,27 @@ ExternalReference ExternalReference::new_space_allocation_limit_address() { return ExternalReference(Heap::NewSpaceAllocationLimitAddress()); } + +ExternalReference ExternalReference::handle_scope_extensions_address() { + return ExternalReference(HandleScope::current_extensions_address()); +} + + +ExternalReference ExternalReference::handle_scope_next_address() { + return ExternalReference(HandleScope::current_next_address()); +} + + +ExternalReference ExternalReference::handle_scope_limit_address() { + return ExternalReference(HandleScope::current_limit_address()); +} + + +ExternalReference ExternalReference::scheduled_exception_address() { + return ExternalReference(Top::scheduled_exception_address()); +} + + #ifdef V8_NATIVE_REGEXP ExternalReference ExternalReference::re_check_stack_guard_state() { diff --git a/src/assembler.h b/src/assembler.h index 21a66dd50..311dadd53 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -373,6 +373,8 @@ class ExternalReference BASE_EMBEDDED { public: explicit ExternalReference(Builtins::CFunctionId id); + explicit ExternalReference(ApiFunction* ptr); + explicit ExternalReference(Builtins::Name name); explicit ExternalReference(Runtime::FunctionId id); @@ -422,6 +424,12 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference double_fp_operation(Token::Value operation); static ExternalReference compare_doubles(); + static ExternalReference handle_scope_extensions_address(); + static ExternalReference handle_scope_next_address(); + static ExternalReference handle_scope_limit_address(); + + static ExternalReference scheduled_exception_address(); + Address address() const {return reinterpret_cast
(address_);} #ifdef ENABLE_DEBUGGER_SUPPORT diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 73ff0115f..7a2f85945 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -36,10 +36,27 @@ namespace v8 { namespace internal { Handle CodeStub::GetCode() { - uint32_t key = GetKey(); - int index = Heap::code_stubs()->FindEntry(key); - if (index == NumberDictionary::kNotFound) { - HandleScope scope; + bool custom_cache = has_custom_cache(); + + int index = 0; + uint32_t key = 0; + if (custom_cache) { + Code* cached; + if (GetCustomCache(&cached)) { + return Handle(cached); + } else { + index = NumberDictionary::kNotFound; + } + } else { + key = GetKey(); + index = Heap::code_stubs()->FindEntry(key); + if (index != NumberDictionary::kNotFound) + return Handle(Code::cast(Heap::code_stubs()->ValueAt(index))); + } + + Code* result; + { + v8::HandleScope scope; // Update the static counter each time a new code stub is generated. Counters::code_stubs.Increment(); @@ -79,18 +96,21 @@ Handle CodeStub::GetCode() { } #endif - // Update the dictionary and the root in Heap. - Handle dict = - Factory::DictionaryAtNumberPut( - Handle(Heap::code_stubs()), - key, - code); - Heap::public_set_code_stubs(*dict); - index = Heap::code_stubs()->FindEntry(key); + if (custom_cache) { + SetCustomCache(*code); + } else { + // Update the dictionary and the root in Heap. + Handle dict = + Factory::DictionaryAtNumberPut( + Handle(Heap::code_stubs()), + key, + code); + Heap::public_set_code_stubs(*dict); + } + result = *code; } - ASSERT(index != NumberDictionary::kNotFound); - return Handle(Code::cast(Heap::code_stubs()->ValueAt(index))); + return Handle(result); } diff --git a/src/code-stubs.h b/src/code-stubs.h index 121140d1b..63461bc0c 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -75,6 +75,7 @@ class CodeStub BASE_EMBEDDED { #define DEF_ENUM(name) name, CODE_STUB_LIST(DEF_ENUM) #undef DEF_ENUM + NoCache, // marker for stubs that do custom caching NUMBER_OF_IDS }; @@ -91,6 +92,12 @@ class CodeStub BASE_EMBEDDED { virtual ~CodeStub() {} + // Override these methods to provide a custom caching mechanism for + // an individual type of code stub. + virtual bool GetCustomCache(Code** code_out) { return false; } + virtual void SetCustomCache(Code* value) { } + virtual bool has_custom_cache() { return false; } + protected: static const int kMajorBits = 5; static const int kMinorBits = kBitsPerInt - kSmiTagSize - kMajorBits; diff --git a/src/codegen.cc b/src/codegen.cc index 57d5f5189..732615f74 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -552,4 +552,20 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { } +bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { + Object* cache = info()->load_stub_cache(); + if (cache->IsUndefined()) { + return false; + } else { + *code_out = Code::cast(cache); + return true; + } +} + + +void ApiGetterEntryStub::SetCustomCache(Code* value) { + info()->set_load_stub_cache(value); +} + + } } // namespace v8::internal diff --git a/src/codegen.h b/src/codegen.h index 8c1b73367..fc4a53b2e 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -301,7 +301,7 @@ class CEntryStub : public CodeStub { Label* throw_normal_exception, Label* throw_termination_exception, Label* throw_out_of_memory_exception, - StackFrame::Type frame_type, + ExitFrame::Mode mode, bool do_gc, bool always_allocate_scope); void GenerateThrowTOS(MacroAssembler* masm); @@ -320,6 +320,32 @@ class CEntryStub : public CodeStub { }; +class ApiGetterEntryStub : public CodeStub { + public: + ApiGetterEntryStub(Handle info, + ApiFunction* fun) + : info_(info), + fun_(fun) { } + void Generate(MacroAssembler* masm); + virtual bool has_custom_cache() { return true; } + virtual bool GetCustomCache(Code** code_out); + virtual void SetCustomCache(Code* value); + + static const int kStackSpace = 6; + static const int kArgc = 4; + private: + Handle info() { return info_; } + ApiFunction* fun() { return fun_; } + Major MajorKey() { return NoCache; } + int MinorKey() { return 0; } + const char* GetName() { return "ApiEntryStub"; } + // The accessor info associated with the function. + Handle info_; + // The function to be called. + ApiFunction* fun_; +}; + + class CEntryDebugBreakStub : public CEntryStub { public: CEntryDebugBreakStub() : CEntryStub(1) { } diff --git a/src/frames.cc b/src/frames.cc index 5cd83324c..d7302dea3 100644 --- a/src/frames.cc +++ b/src/frames.cc @@ -393,8 +393,19 @@ Code* EntryConstructFrame::code() const { } +Object*& ExitFrame::code_slot() const { + const int offset = ExitFrameConstants::kCodeOffset; + return Memory::Object_at(fp() + offset); +} + + Code* ExitFrame::code() const { - return Heap::c_entry_code(); + Object* code = code_slot(); + if (code->IsSmi()) { + return Heap::c_entry_debug_break_code(); + } else { + return Code::cast(code); + } } @@ -412,11 +423,6 @@ Address ExitFrame::GetCallerStackPointer() const { } -Code* ExitDebugFrame::code() const { - return Heap::c_entry_debug_break_code(); -} - - Address StandardFrame::GetExpressionAddress(int n) const { const int offset = StandardFrameConstants::kExpressionsOffset; return fp() + offset - n * kPointerSize; diff --git a/src/frames.h b/src/frames.h index 768196d3c..024065abf 100644 --- a/src/frames.h +++ b/src/frames.h @@ -93,7 +93,6 @@ class StackHandler BASE_EMBEDDED { V(ENTRY, EntryFrame) \ V(ENTRY_CONSTRUCT, EntryConstructFrame) \ V(EXIT, ExitFrame) \ - V(EXIT_DEBUG, ExitDebugFrame) \ V(JAVA_SCRIPT, JavaScriptFrame) \ V(INTERNAL, InternalFrame) \ V(CONSTRUCT, ConstructFrame) \ @@ -119,7 +118,6 @@ class StackFrame BASE_EMBEDDED { bool is_entry() const { return type() == ENTRY; } bool is_entry_construct() const { return type() == ENTRY_CONSTRUCT; } bool is_exit() const { return type() == EXIT; } - bool is_exit_debug() const { return type() == EXIT_DEBUG; } bool is_java_script() const { return type() == JAVA_SCRIPT; } bool is_arguments_adaptor() const { return type() == ARGUMENTS_ADAPTOR; } bool is_internal() const { return type() == INTERNAL; } @@ -260,10 +258,13 @@ class EntryConstructFrame: public EntryFrame { // Exit frames are used to exit JavaScript execution and go to C. class ExitFrame: public StackFrame { public: + enum Mode { MODE_NORMAL, MODE_DEBUG }; virtual Type type() const { return EXIT; } virtual Code* code() const; + Object*& code_slot() const; + // Garbage collection support. virtual void Iterate(ObjectVisitor* v) const; @@ -289,26 +290,6 @@ class ExitFrame: public StackFrame { }; -class ExitDebugFrame: public ExitFrame { - public: - virtual Type type() const { return EXIT_DEBUG; } - - virtual Code* code() const; - - static ExitDebugFrame* cast(StackFrame* frame) { - ASSERT(frame->is_exit_debug()); - return static_cast(frame); - } - - protected: - explicit ExitDebugFrame(StackFrameIterator* iterator) - : ExitFrame(iterator) { } - - private: - friend class StackFrameIterator; -}; - - class StandardFrame: public StackFrame { public: // Testers. diff --git a/src/globals.h b/src/globals.h index fbb648f5b..ae10b72de 100644 --- a/src/globals.h +++ b/src/globals.h @@ -103,6 +103,10 @@ typedef byte* Address; #define V8PRIxPTR "lx" #endif +#if defined(__APPLE__) && defined(__MACH__) +#define USING_MAC_ABI +#endif + // Code-point values in Unicode 4.0 are 21 bits wide. typedef uint16_t uc16; typedef int32_t uc32; diff --git a/src/handles.cc b/src/handles.cc index b764334e8..275fe6a7c 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -105,6 +105,21 @@ void HandleScope::ZapRange(Object** start, Object** end) { } +Address HandleScope::current_extensions_address() { + return reinterpret_cast
(¤t_.extensions); +} + + +Address HandleScope::current_next_address() { + return reinterpret_cast
(¤t_.next); +} + + +Address HandleScope::current_limit_address() { + return reinterpret_cast
(¤t_.limit); +} + + Handle AddKeysFromJSArray(Handle content, Handle array) { CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray); diff --git a/src/handles.h b/src/handles.h index 5d574657c..d3e9b788b 100644 --- a/src/handles.h +++ b/src/handles.h @@ -133,6 +133,13 @@ class HandleScope { return result; } + // Deallocates any extensions used by the current scope. + static void DeleteExtensions(); + + static Address current_extensions_address(); + static Address current_next_address(); + static Address current_limit_address(); + private: // Prevent heap allocation or illegal handle scopes. HandleScope(const HandleScope&); @@ -166,9 +173,6 @@ class HandleScope { // Extend the handle scope making room for more handles. static internal::Object** Extend(); - // Deallocates any extensions used by the current scope. - static void DeleteExtensions(); - // Zaps the handles in the half-open interval [start, end). static void ZapRange(internal::Object** start, internal::Object** end); diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 64efa08b7..8ec9f2d73 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -7686,11 +7686,84 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } +// If true, a Handle passed by value is passed and returned by +// using the location_ field directly. If false, it is passed and +// returned as a pointer to a handle. +#ifdef USING_MAC_ABI +static const bool kPassHandlesDirectly = true; +#else +static const bool kPassHandlesDirectly = false; +#endif + + +void ApiGetterEntryStub::Generate(MacroAssembler* masm) { + Label get_result; + Label prologue; + Label promote_scheduled_exception; + __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc); + ASSERT_EQ(kArgc, 4); + if (kPassHandlesDirectly) { + // When handles as passed directly we don't have to allocate extra + // space for and pass an out parameter. + __ mov(Operand(esp, 0 * kPointerSize), ebx); // name. + __ mov(Operand(esp, 1 * kPointerSize), eax); // arguments pointer. + } else { + // The function expects three arguments to be passed but we allocate + // four to get space for the output cell. The argument slots are filled + // as follows: + // + // 3: output cell + // 2: arguments pointer + // 1: name + // 0: pointer to the output cell + // + // Note that this is one more "argument" than the function expects + // so the out cell will have to be popped explicitly after returning + // from the function. + __ mov(Operand(esp, 1 * kPointerSize), ebx); // name. + __ mov(Operand(esp, 2 * kPointerSize), eax); // arguments pointer. + __ mov(ebx, esp); + __ add(Operand(ebx), Immediate(3 * kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), ebx); // output + __ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); // out cell. + } + // Call the api function! + __ call(fun()->address(), RelocInfo::RUNTIME_ENTRY); + // Check if the function scheduled an exception. + ExternalReference scheduled_exception_address = + ExternalReference::scheduled_exception_address(); + __ cmp(Operand::StaticVariable(scheduled_exception_address), + Immediate(Factory::the_hole_value())); + __ j(not_equal, &promote_scheduled_exception, not_taken); + if (!kPassHandlesDirectly) { + // The returned value is a pointer to the handle holding the result. + // Dereference this to get to the location. + __ mov(eax, Operand(eax, 0)); + } + // Check if the result handle holds 0 + __ test(eax, Operand(eax)); + __ j(not_zero, &get_result, taken); + // It was zero; the result is undefined. + __ mov(eax, Factory::undefined_value()); + __ jmp(&prologue); + // It was non-zero. Dereference to get the result value. + __ bind(&get_result); + __ mov(eax, Operand(eax, 0)); + __ bind(&prologue); + __ LeaveExitFrame(ExitFrame::MODE_NORMAL); + __ ret(0); + __ bind(&promote_scheduled_exception); + __ TailCallRuntime(ExternalReference(Runtime::kPromoteScheduledException), + 0, + 1); +} + + void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, Label* throw_out_of_memory_exception, - StackFrame::Type frame_type, + ExitFrame::Mode mode, bool do_gc, bool always_allocate_scope) { // eax: result parameter for PerformGC, if any @@ -7740,7 +7813,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(zero, &failure_returned, not_taken); // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(frame_type); + __ LeaveExitFrame(mode); __ ret(0); // Handling of failure. @@ -7839,12 +7912,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // of a proper result. The builtin entry handles this by performing // a garbage collection and retrying the builtin (twice). - StackFrame::Type frame_type = is_debug_break ? - StackFrame::EXIT_DEBUG : - StackFrame::EXIT; + ExitFrame::Mode mode = is_debug_break + ? ExitFrame::MODE_DEBUG + : ExitFrame::MODE_NORMAL; // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(frame_type); + __ EnterExitFrame(mode); // eax: result parameter for PerformGC, if any (setup below) // ebx: pointer to builtin function (C callee-saved) @@ -7862,7 +7935,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, false, false); @@ -7871,7 +7944,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, false); @@ -7882,7 +7955,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, true); diff --git a/src/ia32/frames-ia32.cc b/src/ia32/frames-ia32.cc index dea439f24..5c900bedd 100644 --- a/src/ia32/frames-ia32.cc +++ b/src/ia32/frames-ia32.cc @@ -56,19 +56,14 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { state->fp = fp; state->sp = sp; state->pc_address = reinterpret_cast(sp - 1 * kPointerSize); - // Determine frame type. - if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) { - return EXIT_DEBUG; - } else { - return EXIT; - } + return EXIT; } void ExitFrame::Iterate(ObjectVisitor* v) const { - // Exit frames on IA-32 do not contain any pointers. The arguments - // are traversed as part of the expression stack of the calling - // frame. + v->VisitPointer(&code_slot()); + // The arguments are traversed as part of the expression stack of + // the calling frame. } diff --git a/src/ia32/frames-ia32.h b/src/ia32/frames-ia32.h index 3a7c86bf7..c3fe6c748 100644 --- a/src/ia32/frames-ia32.h +++ b/src/ia32/frames-ia32.h @@ -76,7 +76,7 @@ class EntryFrameConstants : public AllStatic { class ExitFrameConstants : public AllStatic { public: - static const int kDebugMarkOffset = -2 * kPointerSize; + static const int kCodeOffset = -2 * kPointerSize; static const int kSPOffset = -1 * kPointerSize; static const int kCallerFPOffset = 0 * kPointerSize; diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 08c4c0c51..34d4fd5f6 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -355,10 +355,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { leave(); } - -void MacroAssembler::EnterExitFrame(StackFrame::Type type) { - ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); - +void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) { // Setup the frame structure on the stack. ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); @@ -369,23 +366,24 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { // Reserve room for entry stack pointer and push the debug marker. ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); push(Immediate(0)); // saved entry sp, patched before call - push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0)); + if (mode == ExitFrame::MODE_DEBUG) { + push(Immediate(0)); + } else { + push(Immediate(CodeObject())); + } // Save the frame pointer and the context in top. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); ExternalReference context_address(Top::k_context_address); mov(Operand::StaticVariable(c_entry_fp_address), ebp); mov(Operand::StaticVariable(context_address), esi); +} - // Setup argc and argv in callee-saved registers. - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - mov(edi, Operand(eax)); - lea(esi, Operand(ebp, eax, times_4, offset)); - +void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc) { #ifdef ENABLE_DEBUGGER_SUPPORT // Save the state of all registers to the stack from the memory // location. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // TODO(1243899): This should be symmetric to // CopyRegistersFromStackToMemory() but it isn't! esp is assumed // correct here, but computed for the other call. Very error @@ -396,8 +394,8 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { } #endif - // Reserve space for two arguments: argc and argv. - sub(Operand(esp), Immediate(2 * kPointerSize)); + // Reserve space for arguments. + sub(Operand(esp), Immediate(argc * kPointerSize)); // Get the required frame alignment for the OS. static const int kFrameAlignment = OS::ActivationFrameAlignment(); @@ -411,15 +409,39 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { } -void MacroAssembler::LeaveExitFrame(StackFrame::Type type) { +void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) { + EnterExitFramePrologue(mode); + + // Setup argc and argv in callee-saved registers. + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + mov(edi, Operand(eax)); + lea(esi, Operand(ebp, eax, times_4, offset)); + + EnterExitFrameEpilogue(mode, 2); +} + + +void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode, + int stack_space, + int argc) { + EnterExitFramePrologue(mode); + + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + lea(esi, Operand(ebp, (stack_space * kPointerSize) + offset)); + + EnterExitFrameEpilogue(mode, argc); +} + + +void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) { #ifdef ENABLE_DEBUGGER_SUPPORT // Restore the memory copy of the registers by digging them out from // the stack. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // It's okay to clobber register ebx below because we don't need // the function pointer after this. const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize; - int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize; + int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize; lea(ebx, Operand(ebp, kOffset)); CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved); } @@ -931,6 +953,48 @@ void MacroAssembler::TailCallRuntime(const ExternalReference& ext, } +void MacroAssembler::PushHandleScope(Register scratch) { + // Push the number of extensions, smi-tagged so the gc will ignore it. + ExternalReference extensions_address = + ExternalReference::handle_scope_extensions_address(); + mov(scratch, Operand::StaticVariable(extensions_address)); + ASSERT_EQ(0, kSmiTag); + shl(scratch, kSmiTagSize); + push(scratch); + mov(Operand::StaticVariable(extensions_address), Immediate(0)); + // Push next and limit pointers which will be wordsize aligned and + // hence automatically smi tagged. + ExternalReference next_address = + ExternalReference::handle_scope_next_address(); + push(Operand::StaticVariable(next_address)); + ExternalReference limit_address = + ExternalReference::handle_scope_limit_address(); + push(Operand::StaticVariable(limit_address)); +} + + +void MacroAssembler::PopHandleScope(Register scratch) { + ExternalReference extensions_address = + ExternalReference::handle_scope_extensions_address(); + Label write_back; + mov(scratch, Operand::StaticVariable(extensions_address)); + cmp(Operand(scratch), Immediate(0)); + j(equal, &write_back); + CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0); + + bind(&write_back); + ExternalReference limit_address = + ExternalReference::handle_scope_limit_address(); + pop(Operand::StaticVariable(limit_address)); + ExternalReference next_address = + ExternalReference::handle_scope_next_address(); + pop(Operand::StaticVariable(next_address)); + pop(scratch); + shr(scratch, kSmiTagSize); + mov(Operand::StaticVariable(extensions_address), scratch); +} + + void MacroAssembler::JumpToRuntime(const ExternalReference& ext) { // Set the entry point and jump to the C entry runtime stub. mov(ebx, Immediate(ext)); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index a0a242806..18d221c40 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -77,16 +77,18 @@ class MacroAssembler: public Assembler { void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter specific kind of exit frame; either EXIT or - // EXIT_DEBUG. Expects the number of arguments in register eax and + // Enter specific kind of exit frame; either in normal or debug mode. + // Expects the number of arguments in register eax and // sets up the number of arguments in register edi and the pointer // to the first argument in register esi. - void EnterExitFrame(StackFrame::Type type); + void EnterExitFrame(ExitFrame::Mode mode); + + void EnterApiExitFrame(ExitFrame::Mode mode, int stack_space, int argc); // Leave the current exit frame. Expects the return value in // register eax:edx (untouched) and the pointer to the first // argument in register esi. - void LeaveExitFrame(StackFrame::Type type); + void LeaveExitFrame(ExitFrame::Mode mode); // --------------------------------------------------------------------------- @@ -269,6 +271,9 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + void PushHandleScope(Register scratch); + void PopHandleScope(Register scratch); + // Jump to a runtime routine. void JumpToRuntime(const ExternalReference& ext); @@ -346,6 +351,9 @@ class MacroAssembler: public Assembler { void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); + void EnterExitFramePrologue(ExitFrame::Mode mode); + void EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc); + // Allocation support helpers. void LoadAllocationTopHelper(Register result, Register result_end, diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index ca4e14210..3e5fc0479 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -776,20 +776,39 @@ void StubCompiler::GenerateLoadCallback(JSObject* object, CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); - // Push the arguments on the JS stack of the caller. - __ pop(scratch2); // remove return address + Handle callback_handle(callback); + + Register other = reg.is(scratch1) ? scratch2 : scratch1; + __ EnterInternalFrame(); + __ PushHandleScope(other); + // Push the stack address where the list of arguments ends + __ mov(other, esp); + __ sub(Operand(other), Immediate(2 * kPointerSize)); + __ push(other); __ push(receiver); // receiver __ push(reg); // holder - __ mov(reg, Immediate(Handle(callback))); // callback data - __ push(reg); - __ push(FieldOperand(reg, AccessorInfo::kDataOffset)); + __ mov(other, Immediate(callback_handle)); + __ push(other); + __ push(FieldOperand(other, AccessorInfo::kDataOffset)); // data __ push(name_reg); // name - __ push(scratch2); // restore return address + // Save a pointer to where we pushed the arguments pointer. + // This will be passed as the const Arguments& to the C++ callback. + __ mov(eax, esp); + __ add(Operand(eax), Immediate(5 * kPointerSize)); + __ mov(ebx, esp); + + // Do call through the api. + ASSERT_EQ(6, ApiGetterEntryStub::kStackSpace); + Address getter_address = v8::ToCData
(callback->getter()); + ApiFunction fun(getter_address); + ApiGetterEntryStub stub(callback_handle, &fun); + __ CallStub(&stub); - // Do tail-call to the runtime system. - ExternalReference load_callback_property = - ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); - __ TailCallRuntime(load_callback_property, 5, 1); + Register tmp = other.is(eax) ? reg : other; + __ PopHandleScope(tmp); + __ LeaveInternalFrame(); + + __ ret(0); } diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 01881346e..10ad2941a 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -979,6 +979,7 @@ void AccessorInfo::AccessorInfoVerify() { VerifyPointer(name()); VerifyPointer(data()); VerifyPointer(flag()); + VerifyPointer(load_stub_cache()); } void AccessorInfo::AccessorInfoPrint() { diff --git a/src/objects-inl.h b/src/objects-inl.h index dca6fc8d3..cb48cbf5d 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2436,6 +2436,7 @@ ACCESSORS(AccessorInfo, setter, Object, kSetterOffset) ACCESSORS(AccessorInfo, data, Object, kDataOffset) ACCESSORS(AccessorInfo, name, Object, kNameOffset) ACCESSORS(AccessorInfo, flag, Smi, kFlagOffset) +ACCESSORS(AccessorInfo, load_stub_cache, Object, kLoadStubCacheOffset) ACCESSORS(AccessCheckInfo, named_callback, Object, kNamedCallbackOffset) ACCESSORS(AccessCheckInfo, indexed_callback, Object, kIndexedCallbackOffset) diff --git a/src/objects.h b/src/objects.h index 232cabf13..ee24b3373 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4724,6 +4724,7 @@ class AccessorInfo: public Struct { DECL_ACCESSORS(data, Object) DECL_ACCESSORS(name, Object) DECL_ACCESSORS(flag, Smi) + DECL_ACCESSORS(load_stub_cache, Object) inline bool all_can_read(); inline void set_all_can_read(bool value); @@ -4749,7 +4750,8 @@ class AccessorInfo: public Struct { static const int kDataOffset = kSetterOffset + kPointerSize; static const int kNameOffset = kDataOffset + kPointerSize; static const int kFlagOffset = kNameOffset + kPointerSize; - static const int kSize = kFlagOffset + kPointerSize; + static const int kLoadStubCacheOffset = kFlagOffset + kPointerSize; + static const int kSize = kLoadStubCacheOffset + kPointerSize; private: // Bit positions in flag. diff --git a/src/runtime.cc b/src/runtime.cc index 2596ae0ee..a21916072 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -4831,6 +4831,12 @@ static Object* Runtime_ReThrow(Arguments args) { } +static Object* Runtime_PromoteScheduledException(Arguments args) { + ASSERT_EQ(0, args.length()); + return Top::PromoteScheduledException(); +} + + static Object* Runtime_ThrowReferenceError(Arguments args) { HandleScope scope; ASSERT(args.length() == 1); @@ -7792,6 +7798,13 @@ static Object* Runtime_Abort(Arguments args) { } +static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) { + ASSERT(args.length() == 0); + HandleScope::DeleteExtensions(); + return Heap::undefined_value(); +} + + #ifdef DEBUG // ListNatives is ONLY used by the fuzz-natives.js in debug mode // Exclude the code in release mode. diff --git a/src/runtime.h b/src/runtime.h index 6b1ce480b..a55ef2501 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -234,6 +234,7 @@ namespace internal { F(ReThrow, 1, 1) \ F(ThrowReferenceError, 1, 1) \ F(StackGuard, 1, 1) \ + F(PromoteScheduledException, 0, 1) \ \ /* Contexts */ \ F(NewContext, 1, 1) \ @@ -263,6 +264,8 @@ namespace internal { F(Log, 2, 1) \ /* ES5 */ \ F(LocalKeys, 1, 1) \ + /* Handle scopes */ \ + F(DeleteHandleScopeExtensions, 0, 1) \ \ /* Pseudo functions - handled as macros by parser */ \ F(IS_VAR, 1, 1) diff --git a/src/stub-cache.cc b/src/stub-cache.cc index e10dc61b2..a399e4563 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -735,11 +735,16 @@ Handle ComputeCallMiss(int argc) { Object* LoadCallbackProperty(Arguments args) { + ASSERT(args[0]->IsJSObject()); + ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[2]); Address getter_address = v8::ToCData
(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); - v8::AccessorInfo info(args.arguments()); + CustomArguments custom_args(callback->data(), + JSObject::cast(args[0]), + JSObject::cast(args[1])); + v8::AccessorInfo info(custom_args.end()); HandleScope scope; v8::Handle result; { diff --git a/src/top.h b/src/top.h index ae94f08e3..0f5aa27b2 100644 --- a/src/top.h +++ b/src/top.h @@ -170,6 +170,10 @@ class Top { return &thread_local_.external_caught_exception_; } + static Object** scheduled_exception_address() { + return &thread_local_.scheduled_exception_; + } + static Object* scheduled_exception() { ASSERT(has_scheduled_exception()); return thread_local_.scheduled_exception_; diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 0721ede00..46b2d8171 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -6750,7 +6750,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, Label* throw_out_of_memory_exception, - StackFrame::Type frame_type, + ExitFrame::Mode mode, bool do_gc, bool always_allocate_scope) { // rax: result parameter for PerformGC, if any. @@ -6833,7 +6833,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(zero, &failure_returned); // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(frame_type, result_size_); + __ LeaveExitFrame(mode, result_size_); __ ret(0); // Handling of failure. @@ -6963,12 +6963,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // this by performing a garbage collection and retrying the // builtin once. - StackFrame::Type frame_type = is_debug_break ? - StackFrame::EXIT_DEBUG : - StackFrame::EXIT; + ExitFrame::Mode mode = is_debug_break ? + ExitFrame::MODE_DEBUG : + ExitFrame::MODE_NORMAL; // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(frame_type, result_size_); + __ EnterExitFrame(mode, result_size_); // rax: Holds the context at this point, but should not be used. // On entry to code generated by GenerateCore, it must hold @@ -6991,7 +6991,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, false, false); @@ -7000,7 +7000,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, false); @@ -7011,7 +7011,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { &throw_normal_exception, &throw_termination_exception, &throw_out_of_memory_exception, - frame_type, + mode, true, true); @@ -7026,6 +7026,11 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { } +void ApiGetterEntryStub::Generate(MacroAssembler* masm) { + UNREACHABLE(); +} + + void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { Label invoke, exit; #ifdef ENABLE_LOGGING_AND_PROFILING diff --git a/src/x64/frames-x64.cc b/src/x64/frames-x64.cc index fe224ad99..6a0527cf6 100644 --- a/src/x64/frames-x64.cc +++ b/src/x64/frames-x64.cc @@ -57,11 +57,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { state->sp = sp; state->pc_address = reinterpret_cast(sp - 1 * kPointerSize); // Determine frame type. - if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) { - return EXIT_DEBUG; - } else { - return EXIT; - } + return EXIT; } int JavaScriptFrame::GetProvidedParametersCount() const { @@ -69,10 +65,10 @@ int JavaScriptFrame::GetProvidedParametersCount() const { } -void ExitFrame::Iterate(ObjectVisitor* a) const { - // Exit frames on X64 do not contain any pointers. The arguments - // are traversed as part of the expression stack of the calling - // frame. +void ExitFrame::Iterate(ObjectVisitor* v) const { + v->VisitPointer(&code_slot()); + // The arguments are traversed as part of the expression stack of + // the calling frame. } byte* InternalFrame::GetCallerStackPointer() const { diff --git a/src/x64/frames-x64.h b/src/x64/frames-x64.h index eefaa0aeb..a92b248d8 100644 --- a/src/x64/frames-x64.h +++ b/src/x64/frames-x64.h @@ -63,7 +63,7 @@ class EntryFrameConstants : public AllStatic { class ExitFrameConstants : public AllStatic { public: - static const int kDebugMarkOffset = -2 * kPointerSize; + static const int kCodeOffset = -2 * kPointerSize; static const int kSPOffset = -1 * kPointerSize; static const int kCallerFPOffset = +0 * kPointerSize; diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index b2f69bb7a..6bf6e6a71 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -1787,9 +1787,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { } -void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) { - ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); - +void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { // Setup the frame structure on the stack. // All constants are relative to the frame pointer of the exit frame. ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); @@ -1801,7 +1799,12 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) { // Reserve room for entry stack pointer and push the debug marker. ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); push(Immediate(0)); // saved entry sp, patched before call - push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0)); + if (mode == ExitFrame::MODE_DEBUG) { + push(Immediate(0)); + } else { + movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT); + push(kScratchRegister); + } // Save the frame pointer and the context in top. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); @@ -1821,7 +1824,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) { #ifdef ENABLE_DEBUGGER_SUPPORT // Save the state of all registers to the stack from the memory // location. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // TODO(1243899): This should be symmetric to // CopyRegistersFromStackToMemory() but it isn't! esp is assumed // correct here, but computed for the other call. Very error @@ -1860,17 +1863,17 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) { } -void MacroAssembler::LeaveExitFrame(StackFrame::Type type, int result_size) { +void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) { // Registers: // r15 : argv #ifdef ENABLE_DEBUGGER_SUPPORT // Restore the memory copy of the registers by digging them out from // the stack. This is needed to allow nested break points. - if (type == StackFrame::EXIT_DEBUG) { + if (mode == ExitFrame::MODE_DEBUG) { // It's okay to clobber register rbx below because we don't need // the function pointer after this. const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize; - int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize; + int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize; lea(rbx, Operand(rbp, kOffset)); CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved); } diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 4c2f35bd9..11cdfc3c4 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -106,16 +106,16 @@ class MacroAssembler: public Assembler { void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter specific kind of exit frame; either EXIT or - // EXIT_DEBUG. Expects the number of arguments in register rax and + // Enter specific kind of exit frame; either in normal or + // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer // to the first argument in register rsi. - void EnterExitFrame(StackFrame::Type type, int result_size = 1); + void EnterExitFrame(ExitFrame::Mode mode, int result_size = 1); // Leave the current exit frame. Expects/provides the return value in // register rax:rdx (untouched) and the pointer to the first // argument in register rsi. - void LeaveExitFrame(StackFrame::Type type, int result_size = 1); + void LeaveExitFrame(ExitFrame::Mode mode, int result_size = 1); // --------------------------------------------------------------------------- diff --git a/test/cctest/SConscript b/test/cctest/SConscript index f041041c1..9deefa554 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -34,6 +34,7 @@ Import('context object_files') SOURCES = { 'all': [ + 'test-accessors.cc', 'test-alloc.cc', 'test-api.cc', 'test-ast.cc', diff --git a/test/cctest/cctest.cc b/test/cctest/cctest.cc index 82a33e6da..f638ed480 100644 --- a/test/cctest/cctest.cc +++ b/test/cctest/cctest.cc @@ -121,3 +121,6 @@ int main(int argc, char* argv[]) { v8::V8::Dispose(); return 0; } + +RegisterThreadedTest *RegisterThreadedTest::first_ = NULL; +int RegisterThreadedTest::count_ = 0; diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h index a95645e01..404b692b2 100644 --- a/test/cctest/cctest.h +++ b/test/cctest/cctest.h @@ -28,6 +28,8 @@ #ifndef CCTEST_H_ #define CCTEST_H_ +#include "v8.h" + #ifndef TEST #define TEST(Name) \ static void Test##Name(); \ @@ -72,4 +74,138 @@ class CcTest { CcTest* prev_; }; +// Switches between all the Api tests using the threading support. +// In order to get a surprising but repeatable pattern of thread +// switching it has extra semaphores to control the order in which +// the tests alternate, not relying solely on the big V8 lock. +// +// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its +// callbacks. This will have no effect when we are not running the +// thread fuzzing test. In the thread fuzzing test it will +// pseudorandomly select a successor thread and switch execution +// to that thread, suspending the current test. +class ApiTestFuzzer: public v8::internal::Thread { + public: + void CallTest(); + explicit ApiTestFuzzer(int num) + : test_number_(num), + gate_(v8::internal::OS::CreateSemaphore(0)), + active_(true) { + } + ~ApiTestFuzzer() { delete gate_; } + + // The ApiTestFuzzer is also a Thread, so it has a Run method. + virtual void Run(); + + enum PartOfTest { FIRST_PART, SECOND_PART }; + + static void Setup(PartOfTest part); + static void RunAllTests(); + static void TearDown(); + // This method switches threads if we are running the Threading test. + // Otherwise it does nothing. + static void Fuzz(); + private: + static bool fuzzing_; + static int tests_being_run_; + static int current_; + static int active_tests_; + static bool NextThread(); + int test_number_; + v8::internal::Semaphore* gate_; + bool active_; + void ContextSwitch(); + static int GetNextTestNumber(); + static v8::internal::Semaphore* all_tests_done_; +}; + + +#define THREADED_TEST(Name) \ + static void Test##Name(); \ + RegisterThreadedTest register_##Name(Test##Name, #Name); \ + /* */ TEST(Name) + + +class RegisterThreadedTest { + public: + explicit RegisterThreadedTest(CcTest::TestFunction* callback, + const char* name) + : fuzzer_(NULL), callback_(callback), name_(name) { + prev_ = first_; + first_ = this; + count_++; + } + static int count() { return count_; } + static RegisterThreadedTest* nth(int i) { + CHECK(i < count()); + RegisterThreadedTest* current = first_; + while (i > 0) { + i--; + current = current->prev_; + } + return current; + } + CcTest::TestFunction* callback() { return callback_; } + ApiTestFuzzer* fuzzer_; + const char* name() { return name_; } + + private: + static RegisterThreadedTest* first_; + static int count_; + CcTest::TestFunction* callback_; + RegisterThreadedTest* prev_; + const char* name_; +}; + + +// A LocalContext holds a reference to a v8::Context. +class LocalContext { + public: + LocalContext(v8::ExtensionConfiguration* extensions = 0, + v8::Handle global_template = + v8::Handle(), + v8::Handle global_object = v8::Handle()) + : context_(v8::Context::New(extensions, global_template, global_object)) { + context_->Enter(); + } + + virtual ~LocalContext() { + context_->Exit(); + context_.Dispose(); + } + + v8::Context* operator->() { return *context_; } + v8::Context* operator*() { return *context_; } + bool IsReady() { return !context_.IsEmpty(); } + + v8::Local local() { + return v8::Local::New(context_); + } + + private: + v8::Persistent context_; +}; + + +static inline v8::Local v8_num(double x) { + return v8::Number::New(x); +} + + +static inline v8::Local v8_str(const char* x) { + return v8::String::New(x); +} + + +static inline v8::Local v8_compile(const char* x) { + return v8::Script::Compile(v8_str(x)); +} + + +// Helper function that compiles and runs the source. +static inline v8::Local CompileRun(const char* source) { + return v8::Script::Compile(v8::String::New(source))->Run(); +} + + #endif // ifndef CCTEST_H_ diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 53df07297..dba2adaec 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -38,6 +38,8 @@ #include "utils.h" #include "cctest.h" +static const bool kLogThreading = false; + static bool IsNaN(double x) { #ifdef WIN32 return _isnan(x); @@ -58,131 +60,6 @@ using ::v8::Extension; namespace i = ::v8::internal; -static Local v8_num(double x) { - return v8::Number::New(x); -} - - -static Local v8_str(const char* x) { - return String::New(x); -} - - -static Local