}
-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<v8::Value> data,
v8::Local<v8::Object> holder,
v8::Local<v8::Function> callee,
Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
- ExitFrame::Mode mode,
+ StackFrame::Type frame_type,
bool do_gc,
bool always_allocate) {
// r0: result parameter for PerformGC, if any
// r0:r1: result
// sp: stack pointer
// fp: frame pointer
- __ LeaveExitFrame(mode);
+ __ LeaveExitFrame(frame_type);
// check if we should retry or throw exception
Label retry;
// this by performing a garbage collection and retrying the
// builtin once.
- ExitFrame::Mode mode = is_debug_break
- ? ExitFrame::MODE_DEBUG
- : ExitFrame::MODE_NORMAL;
+ StackFrame::Type frame_type = is_debug_break
+ ? StackFrame::EXIT_DEBUG
+ : StackFrame::EXIT;
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode);
+ __ EnterExitFrame(frame_type);
// r4: number of arguments (C callee-saved)
// r5: pointer to builtin function (C callee-saved)
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
false,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
true);
if (fp == 0) return NONE;
// Compute frame type and stack pointer.
Address sp = fp + ExitFrameConstants::kSPDisplacement;
- const int offset = ExitFrameConstants::kCodeOffset;
- Object* code = Memory::Object_at(fp + offset);
- bool is_debug_exit = code->IsSmi();
- if (is_debug_exit) {
+ Type type;
+ if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
+ type = EXIT_DEBUG;
sp -= kNumJSCallerSaved * kPointerSize;
+ } else {
+ type = EXIT;
}
// Fill in the state.
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
- return EXIT;
+ return type;
}
void ExitFrame::Iterate(ObjectVisitor* v) const {
- v->VisitPointer(&code_slot());
- // The arguments are traversed as part of the expression stack of
- // the calling frame.
+ // Do nothing
}
static const int kSPDisplacement = -1 * kPointerSize;
// The debug marker is just above the frame pointer.
- static const int kCodeOffset = -1 * kPointerSize;
+ static const int kDebugMarkOffset = -1 * kPointerSize;
static const int kSavedRegistersOffset = 0 * kPointerSize;
}
-void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
+void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
+ ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
+
// Compute the argv pointer and keep it in a callee-saved register.
// r0 is argc.
add(r6, sp, Operand(r0, LSL, kPointerSizeLog2));
stm(db_w, sp, fp.bit() | ip.bit() | lr.bit());
mov(fp, Operand(sp)); // setup new frame pointer
- if (mode == ExitFrame::MODE_DEBUG) {
- mov(ip, Operand(Smi::FromInt(0)));
- } else {
- mov(ip, Operand(CodeObject()));
- }
+ // Push debug marker.
+ mov(ip, Operand(type == StackFrame::EXIT_DEBUG ? 1 : 0));
push(ip);
// Save the frame pointer and the context in top.
#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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_DEBUG) {
// Use sp as base to push.
CopyRegistersFromMemoryToStack(sp, kJSCallerSaved);
}
}
-void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
+void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
#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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_DEBUG) {
// This code intentionally clobbers r2 and r3.
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
- const int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
+ const int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
add(r3, fp, Operand(kOffset));
CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved);
}
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
- // Enter specific kind of exit frame; either normal or debug mode.
- // Expects the number of arguments in register r0 and
+ // Enter specific kind of exit frame; either EXIT or
+ // EXIT_DEBUG. 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(ExitFrame::Mode mode);
+ void EnterExitFrame(StackFrame::Type type);
// Leave the current exit frame. Expects the return value in r0.
- void LeaveExitFrame(ExitFrame::Mode mode);
+ void LeaveExitFrame(StackFrame::Type type);
// Align the stack by optionally pushing a Smi zero.
void AlignStack(int offset);
: 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)) {}
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() {
public:
explicit ExternalReference(Builtins::CFunctionId id);
- explicit ExternalReference(ApiFunction* ptr);
-
explicit ExternalReference(Builtins::Name name);
explicit ExternalReference(Runtime::FunctionId id);
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>(address_);}
#ifdef ENABLE_DEBUGGER_SUPPORT
namespace internal {
Handle<Code> CodeStub::GetCode() {
- bool custom_cache = has_custom_cache();
-
- int index = 0;
- uint32_t key = 0;
- if (custom_cache) {
- Code* cached;
- if (GetCustomCache(&cached)) {
- return Handle<Code>(cached);
- } else {
- index = NumberDictionary::kNotFound;
- }
- } else {
- key = GetKey();
- index = Heap::code_stubs()->FindEntry(key);
- if (index != NumberDictionary::kNotFound)
- return Handle<Code>(Code::cast(Heap::code_stubs()->ValueAt(index)));
- }
-
- Code* result;
- {
- v8::HandleScope scope;
+ uint32_t key = GetKey();
+ int index = Heap::code_stubs()->FindEntry(key);
+ if (index == NumberDictionary::kNotFound) {
+ HandleScope scope;
// Update the static counter each time a new code stub is generated.
Counters::code_stubs.Increment();
}
#endif
- if (custom_cache) {
- SetCustomCache(*code);
- } else {
- // Update the dictionary and the root in Heap.
- Handle<NumberDictionary> dict =
- Factory::DictionaryAtNumberPut(
- Handle<NumberDictionary>(Heap::code_stubs()),
- key,
- code);
- Heap::public_set_code_stubs(*dict);
- }
- result = *code;
+ // Update the dictionary and the root in Heap.
+ Handle<NumberDictionary> dict =
+ Factory::DictionaryAtNumberPut(
+ Handle<NumberDictionary>(Heap::code_stubs()),
+ key,
+ code);
+ Heap::public_set_code_stubs(*dict);
+ index = Heap::code_stubs()->FindEntry(key);
}
+ ASSERT(index != NumberDictionary::kNotFound);
- return Handle<Code>(result);
+ return Handle<Code>(Code::cast(Heap::code_stubs()->ValueAt(index)));
}
#define DEF_ENUM(name) name,
CODE_STUB_LIST(DEF_ENUM)
#undef DEF_ENUM
- NoCache, // marker for stubs that do custom caching
NUMBER_OF_IDS
};
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;
}
-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
Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
- ExitFrame::Mode mode,
+ StackFrame::Type frame_type,
bool do_gc,
bool always_allocate_scope);
void GenerateThrowTOS(MacroAssembler* masm);
};
-class ApiGetterEntryStub : public CodeStub {
- public:
- ApiGetterEntryStub(Handle<AccessorInfo> 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<AccessorInfo> 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<AccessorInfo> info_;
- // The function to be called.
- ApiFunction* fun_;
-};
-
-
class CEntryDebugBreakStub : public CEntryStub {
public:
CEntryDebugBreakStub() : CEntryStub(1) { }
}
-Object*& ExitFrame::code_slot() const {
- const int offset = ExitFrameConstants::kCodeOffset;
- return Memory::Object_at(fp() + offset);
-}
-
-
Code* ExitFrame::code() const {
- Object* code = code_slot();
- if (code->IsSmi()) {
- return Heap::c_entry_debug_break_code();
- } else {
- return Code::cast(code);
- }
+ return Heap::c_entry_code();
}
}
+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;
V(ENTRY, EntryFrame) \
V(ENTRY_CONSTRUCT, EntryConstructFrame) \
V(EXIT, ExitFrame) \
+ V(EXIT_DEBUG, ExitDebugFrame) \
V(JAVA_SCRIPT, JavaScriptFrame) \
V(INTERNAL, InternalFrame) \
V(CONSTRUCT, ConstructFrame) \
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; }
// 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;
};
+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<ExitDebugFrame*>(frame);
+ }
+
+ protected:
+ explicit ExitDebugFrame(StackFrameIterator* iterator)
+ : ExitFrame(iterator) { }
+
+ private:
+ friend class StackFrameIterator;
+};
+
+
class StandardFrame: public StackFrame {
public:
// Testers.
#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;
}
-Address HandleScope::current_extensions_address() {
- return reinterpret_cast<Address>(¤t_.extensions);
-}
-
-
-Address HandleScope::current_next_address() {
- return reinterpret_cast<Address>(¤t_.next);
-}
-
-
-Address HandleScope::current_limit_address() {
- return reinterpret_cast<Address>(¤t_.limit);
-}
-
-
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
Handle<JSArray> array) {
CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray);
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&);
// 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);
}
-// If true, a Handle<T> 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,
- ExitFrame::Mode mode,
+ StackFrame::Type frame_type,
bool do_gc,
bool always_allocate_scope) {
// eax: result parameter for PerformGC, if any
__ j(zero, &failure_returned, not_taken);
// Exit the JavaScript to C++ exit frame.
- __ LeaveExitFrame(mode);
+ __ LeaveExitFrame(frame_type);
__ ret(0);
// Handling of failure.
// of a proper result. The builtin entry handles this by performing
// a garbage collection and retrying the builtin (twice).
- ExitFrame::Mode mode = is_debug_break
- ? ExitFrame::MODE_DEBUG
- : ExitFrame::MODE_NORMAL;
+ StackFrame::Type frame_type = is_debug_break ?
+ StackFrame::EXIT_DEBUG :
+ StackFrame::EXIT;
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode);
+ __ EnterExitFrame(frame_type);
// eax: result parameter for PerformGC, if any (setup below)
// ebx: pointer to builtin function (C callee-saved)
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
false,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
true);
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
- return EXIT;
+ // Determine frame type.
+ if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
+ return EXIT_DEBUG;
+ } else {
+ return EXIT;
+ }
}
void ExitFrame::Iterate(ObjectVisitor* v) const {
- v->VisitPointer(&code_slot());
- // The arguments are traversed as part of the expression stack of
- // the calling frame.
+ // Exit frames on IA-32 do not contain any pointers. The arguments
+ // are traversed as part of the expression stack of the calling
+ // frame.
}
class ExitFrameConstants : public AllStatic {
public:
- static const int kCodeOffset = -2 * kPointerSize;
+ static const int kDebugMarkOffset = -2 * kPointerSize;
static const int kSPOffset = -1 * kPointerSize;
static const int kCallerFPOffset = 0 * kPointerSize;
leave();
}
-void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
+
+void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
+ ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
+
// Setup the frame structure on the stack.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
// 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
- if (mode == ExitFrame::MODE_DEBUG) {
- push(Immediate(0));
- } else {
- push(Immediate(CodeObject()));
- }
+ push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
// 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);
-}
-void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc) {
+ // 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));
+
#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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_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
}
#endif
- // Reserve space for arguments.
- sub(Operand(esp), Immediate(argc * kPointerSize));
+ // Reserve space for two arguments: argc and argv.
+ sub(Operand(esp), Immediate(2 * kPointerSize));
// Get the required frame alignment for the OS.
static const int kFrameAlignment = OS::ActivationFrameAlignment();
}
-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) {
+void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
#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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_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::kCodeOffset - kCallerSavedSize;
+ int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
lea(ebx, Operand(ebp, kOffset));
CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved);
}
}
-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));
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
- // Enter specific kind of exit frame; either in normal or debug mode.
- // Expects the number of arguments in register eax and
+ // Enter specific kind of exit frame; either EXIT or
+ // EXIT_DEBUG. 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(ExitFrame::Mode mode);
-
- void EnterApiExitFrame(ExitFrame::Mode mode, int stack_space, int argc);
+ void EnterExitFrame(StackFrame::Type type);
// 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(ExitFrame::Mode mode);
+ void LeaveExitFrame(StackFrame::Type type);
// ---------------------------------------------------------------------------
int num_arguments,
int result_size);
- void PushHandleScope(Register scratch);
- void PopHandleScope(Register scratch);
-
// Jump to a runtime routine.
void JumpToRuntime(const ExternalReference& ext);
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,
CheckPrototypes(object, receiver, holder,
scratch1, scratch2, name, miss);
- Handle<AccessorInfo> 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 the arguments on the JS stack of the caller.
+ __ pop(scratch2); // remove return address
__ push(receiver); // receiver
__ push(reg); // holder
- __ mov(other, Immediate(callback_handle));
- __ push(other);
- __ push(FieldOperand(other, AccessorInfo::kDataOffset)); // data
+ __ mov(reg, Immediate(Handle<AccessorInfo>(callback))); // callback data
+ __ push(reg);
+ __ push(FieldOperand(reg, AccessorInfo::kDataOffset));
__ push(name_reg); // name
- // 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<Address>(callback->getter());
- ApiFunction fun(getter_address);
- ApiGetterEntryStub stub(callback_handle, &fun);
- __ CallStub(&stub);
+ __ push(scratch2); // restore return address
- Register tmp = other.is(eax) ? reg : other;
- __ PopHandleScope(tmp);
- __ LeaveInternalFrame();
-
- __ ret(0);
+ // Do tail-call to the runtime system.
+ ExternalReference load_callback_property =
+ ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
+ __ TailCallRuntime(load_callback_property, 5, 1);
}
VerifyPointer(name());
VerifyPointer(data());
VerifyPointer(flag());
- VerifyPointer(load_stub_cache());
}
void AccessorInfo::AccessorInfoPrint() {
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)
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);
static const int kDataOffset = kSetterOffset + kPointerSize;
static const int kNameOffset = kDataOffset + kPointerSize;
static const int kFlagOffset = kNameOffset + kPointerSize;
- static const int kLoadStubCacheOffset = kFlagOffset + kPointerSize;
- static const int kSize = kLoadStubCacheOffset + kPointerSize;
+ static const int kSize = kFlagOffset + kPointerSize;
private:
// Bit positions in flag.
}
-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);
}
-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.
F(ReThrow, 1, 1) \
F(ThrowReferenceError, 1, 1) \
F(StackGuard, 1, 1) \
- F(PromoteScheduledException, 0, 1) \
\
/* Contexts */ \
F(NewContext, 1, 1) \
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)
Object* LoadCallbackProperty(Arguments args) {
- ASSERT(args[0]->IsJSObject());
- ASSERT(args[1]->IsJSObject());
AccessorInfo* callback = AccessorInfo::cast(args[2]);
Address getter_address = v8::ToCData<Address>(callback->getter());
v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address);
ASSERT(fun != NULL);
- CustomArguments custom_args(callback->data(),
- JSObject::cast(args[0]),
- JSObject::cast(args[1]));
- v8::AccessorInfo info(custom_args.end());
+ v8::AccessorInfo info(args.arguments());
HandleScope scope;
v8::Handle<v8::Value> result;
{
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_;
Label* throw_normal_exception,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
- ExitFrame::Mode mode,
+ StackFrame::Type frame_type,
bool do_gc,
bool always_allocate_scope) {
// rax: result parameter for PerformGC, if any.
__ j(zero, &failure_returned);
// Exit the JavaScript to C++ exit frame.
- __ LeaveExitFrame(mode, result_size_);
+ __ LeaveExitFrame(frame_type, result_size_);
__ ret(0);
// Handling of failure.
// this by performing a garbage collection and retrying the
// builtin once.
- ExitFrame::Mode mode = is_debug_break ?
- ExitFrame::MODE_DEBUG :
- ExitFrame::MODE_NORMAL;
+ StackFrame::Type frame_type = is_debug_break ?
+ StackFrame::EXIT_DEBUG :
+ StackFrame::EXIT;
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode, result_size_);
+ __ EnterExitFrame(frame_type, result_size_);
// rax: Holds the context at this point, but should not be used.
// On entry to code generated by GenerateCore, it must hold
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
false,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
false);
&throw_normal_exception,
&throw_termination_exception,
&throw_out_of_memory_exception,
- mode,
+ frame_type,
true,
true);
}
-void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
- UNREACHABLE();
-}
-
-
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
Label invoke, exit;
#ifdef ENABLE_LOGGING_AND_PROFILING
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
// Determine frame type.
- return EXIT;
+ if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
+ return EXIT_DEBUG;
+ } else {
+ return EXIT;
+ }
}
int JavaScriptFrame::GetProvidedParametersCount() const {
}
-void ExitFrame::Iterate(ObjectVisitor* v) const {
- v->VisitPointer(&code_slot());
- // The arguments are traversed as part of the expression stack of
- // the calling frame.
+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.
}
byte* InternalFrame::GetCallerStackPointer() const {
class ExitFrameConstants : public AllStatic {
public:
- static const int kCodeOffset = -2 * kPointerSize;
+ static const int kDebugMarkOffset = -2 * kPointerSize;
static const int kSPOffset = -1 * kPointerSize;
static const int kCallerFPOffset = +0 * kPointerSize;
}
-void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
+void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) {
+ ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
+
// Setup the frame structure on the stack.
// All constants are relative to the frame pointer of the exit frame.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
// 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
- if (mode == ExitFrame::MODE_DEBUG) {
- push(Immediate(0));
- } else {
- movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
- push(kScratchRegister);
- }
+ push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
// Save the frame pointer and the context in top.
ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
#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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_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
}
-void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) {
+void MacroAssembler::LeaveExitFrame(StackFrame::Type type, 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 (mode == ExitFrame::MODE_DEBUG) {
+ if (type == StackFrame::EXIT_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::kCodeOffset - kCallerSavedSize;
+ int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
lea(rbx, Operand(rbp, kOffset));
CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
}
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
- // Enter specific kind of exit frame; either in normal or
- // debug mode. Expects the number of arguments in register rax and
+ // Enter specific kind of exit frame; either EXIT or
+ // EXIT_DEBUG. 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(ExitFrame::Mode mode, int result_size = 1);
+ void EnterExitFrame(StackFrame::Type type, 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(ExitFrame::Mode mode, int result_size = 1);
+ void LeaveExitFrame(StackFrame::Type type, int result_size = 1);
// ---------------------------------------------------------------------------
SOURCES = {
'all': [
- 'test-accessors.cc',
'test-alloc.cc',
'test-api.cc',
'test-ast.cc',
v8::V8::Dispose();
return 0;
}
-
-RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
-int RegisterThreadedTest::count_ = 0;
#ifndef CCTEST_H_
#define CCTEST_H_
-#include "v8.h"
-
#ifndef TEST
#define TEST(Name) \
static void Test##Name(); \
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<v8::ObjectTemplate> global_template =
- v8::Handle<v8::ObjectTemplate>(),
- v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>())
- : 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<v8::Context> local() {
- return v8::Local<v8::Context>::New(context_);
- }
-
- private:
- v8::Persistent<v8::Context> context_;
-};
-
-
-static inline v8::Local<v8::Value> v8_num(double x) {
- return v8::Number::New(x);
-}
-
-
-static inline v8::Local<v8::String> v8_str(const char* x) {
- return v8::String::New(x);
-}
-
-
-static inline v8::Local<v8::Script> v8_compile(const char* x) {
- return v8::Script::Compile(v8_str(x));
-}
-
-
-// Helper function that compiles and runs the source.
-static inline v8::Local<v8::Value> CompileRun(const char* source) {
- return v8::Script::Compile(v8::String::New(source))->Run();
-}
-
-
#endif // ifndef CCTEST_H_
+++ /dev/null
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <stdlib.h>
-
-#include "v8.h"
-
-#include "api.h"
-#include "cctest.h"
-#include "frames-inl.h"
-#include "string-stream.h"
-
-using ::v8::ObjectTemplate;
-using ::v8::Value;
-using ::v8::Context;
-using ::v8::Local;
-using ::v8::String;
-using ::v8::Script;
-using ::v8::Function;
-using ::v8::AccessorInfo;
-using ::v8::Extension;
-
-namespace i = ::v8::internal;
-
-static v8::Handle<Value> handle_property(Local<String> name,
- const AccessorInfo&) {
- ApiTestFuzzer::Fuzz();
- return v8_num(900);
-}
-
-
-THREADED_TEST(PropertyHandler) {
- v8::HandleScope scope;
- Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
- fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
- LocalContext env;
- Local<Function> fun = fun_templ->GetFunction();
- env->Global()->Set(v8_str("Fun"), fun);
- Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
- CHECK_EQ(900, getter->Run()->Int32Value());
- Local<Script> setter = v8_compile("obj.foo = 901;");
- CHECK_EQ(901, setter->Run()->Int32Value());
-}
-
-
-static v8::Handle<Value> GetIntValue(Local<String> property,
- const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- int* value =
- static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
- return v8_num(*value);
-}
-
-
-static void SetIntValue(Local<String> property,
- Local<Value> value,
- const AccessorInfo& info) {
- int* field =
- static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
- *field = value->Int32Value();
-}
-
-int foo, bar, baz;
-
-THREADED_TEST(GlobalVariableAccess) {
- foo = 0;
- bar = -4;
- baz = 10;
- v8::HandleScope scope;
- v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
- templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&foo));
- templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&bar));
- templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
- GetIntValue,
- SetIntValue,
- v8::External::New(&baz));
- LocalContext env(0, templ->InstanceTemplate());
- v8_compile("foo = (++bar) + baz")->Run();
- CHECK_EQ(bar, -3);
- CHECK_EQ(foo, 7);
-}
-
-
-static int x_register = 0;
-static v8::Handle<v8::Object> x_receiver;
-static v8::Handle<v8::Object> x_holder;
-
-
-static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- CHECK_EQ(x_receiver, info.This());
- CHECK_EQ(x_holder, info.Holder());
- return v8_num(x_register);
-}
-
-
-static void XSetter(Local<String> name,
- Local<Value> value,
- const AccessorInfo& info) {
- CHECK_EQ(x_holder, info.This());
- CHECK_EQ(x_holder, info.Holder());
- x_register = value->Int32Value();
-}
-
-
-THREADED_TEST(AccessorIC) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("x"), XGetter, XSetter);
- LocalContext context;
- x_holder = obj->NewInstance();
- context->Global()->Set(v8_str("holder"), x_holder);
- x_receiver = v8::Object::New();
- context->Global()->Set(v8_str("obj"), x_receiver);
- v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
- "obj.__proto__ = holder;"
- "var result = [];"
- "for (var i = 0; i < 10; i++) {"
- " holder.x = i;"
- " result.push(obj.x);"
- "}"
- "result"));
- CHECK_EQ(10, array->Length());
- for (int i = 0; i < 10; i++) {
- v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
- CHECK_EQ(v8::Integer::New(i), entry);
- }
-}
-
-
-static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
- Local<String> name,
- const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- return v8::True();
-}
-
-
-THREADED_TEST(AccessorProhibitsOverwriting) {
- v8::HandleScope scope;
- LocalContext context;
- Local<ObjectTemplate> templ = ObjectTemplate::New();
- templ->SetAccessor(v8_str("x"),
- AccessorProhibitsOverwritingGetter,
- 0,
- v8::Handle<Value>(),
- v8::PROHIBITS_OVERWRITING,
- v8::ReadOnly);
- Local<v8::Object> instance = templ->NewInstance();
- context->Global()->Set(v8_str("obj"), instance);
- Local<Value> value = CompileRun(
- "obj.__defineGetter__('x', function() { return false; });"
- "obj.x");
- CHECK(value->BooleanValue());
- value = CompileRun(
- "var setter_called = false;"
- "obj.__defineSetter__('x', function() { setter_called = true; });"
- "obj.x = 42;"
- "setter_called");
- CHECK(!value->BooleanValue());
- value = CompileRun(
- "obj2 = {};"
- "obj2.__proto__ = obj;"
- "obj2.__defineGetter__('x', function() { return false; });"
- "obj2.x");
- CHECK(value->BooleanValue());
- value = CompileRun(
- "var setter_called = false;"
- "obj2 = {};"
- "obj2.__proto__ = obj;"
- "obj2.__defineSetter__('x', function() { setter_called = true; });"
- "obj2.x = 42;"
- "setter_called");
- CHECK(!value->BooleanValue());
-}
-
-
-template <int C>
-static v8::Handle<Value> HandleAllocatingGetter(Local<String> name,
- const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- for (int i = 0; i < C; i++)
- v8::String::New("foo");
- return v8::String::New("foo");
-}
-
-
-THREADED_TEST(HandleScopePop) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>);
- obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>);
- LocalContext context;
- v8::Handle<v8::Object> inst = obj->NewInstance();
- context->Global()->Set(v8::String::New("obj"), inst);
- int count_before = i::HandleScope::NumberOfHandles();
- {
- v8::HandleScope scope;
- CompileRun(
- "for (var i = 0; i < 1000; i++) {"
- " obj.one;"
- " obj.many;"
- "}");
- }
- int count_after = i::HandleScope::NumberOfHandles();
- CHECK_EQ(count_before, count_after);
-}
-
-static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name,
- const AccessorInfo& info) {
- CHECK(info.This() == info.Holder());
- CHECK(info.Data()->Equals(v8::String::New("data")));
- ApiTestFuzzer::Fuzz();
- CHECK(info.This() == info.Holder());
- CHECK(info.Data()->Equals(v8::String::New("data")));
- i::Heap::CollectAllGarbage(true);
- CHECK(info.This() == info.Holder());
- CHECK(info.Data()->Equals(v8::String::New("data")));
- return v8::Integer::New(17);
-}
-
-THREADED_TEST(DirectCall) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("xxx"),
- CheckAccessorArgsCorrect,
- NULL,
- v8::String::New("data"));
- LocalContext context;
- v8::Handle<v8::Object> inst = obj->NewInstance();
- context->Global()->Set(v8::String::New("obj"), inst);
- Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
- for (int i = 0; i < 10; i++) {
- Local<Value> result = scr->Run();
- CHECK(!result.IsEmpty());
- CHECK_EQ(17, result->Int32Value());
- }
-}
-
-static v8::Handle<Value> EmptyGetter(Local<String> name,
- const AccessorInfo& info) {
- CheckAccessorArgsCorrect(name, info);
- ApiTestFuzzer::Fuzz();
- CheckAccessorArgsCorrect(name, info);
- return v8::Handle<v8::Value>();
-}
-
-THREADED_TEST(EmptyResult) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
- LocalContext context;
- v8::Handle<v8::Object> inst = obj->NewInstance();
- context->Global()->Set(v8::String::New("obj"), inst);
- Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
- for (int i = 0; i < 10; i++) {
- Local<Value> result = scr->Run();
- CHECK(result == v8::Undefined());
- }
-}
-
-
-THREADED_TEST(NoReuseRegress) {
- // Check that the IC generated for the one test doesn't get reused
- // for the other.
- v8::HandleScope scope;
- {
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
- LocalContext context;
- v8::Handle<v8::Object> inst = obj->NewInstance();
- context->Global()->Set(v8::String::New("obj"), inst);
- Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
- for (int i = 0; i < 2; i++) {
- Local<Value> result = scr->Run();
- CHECK(result == v8::Undefined());
- }
- }
- {
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("xxx"),
- CheckAccessorArgsCorrect,
- NULL,
- v8::String::New("data"));
- LocalContext context;
- v8::Handle<v8::Object> inst = obj->NewInstance();
- context->Global()->Set(v8::String::New("obj"), inst);
- Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
- for (int i = 0; i < 10; i++) {
- Local<Value> result = scr->Run();
- CHECK(!result.IsEmpty());
- CHECK_EQ(17, result->Int32Value());
- }
- }
-}
-
-static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
- const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- return v8::ThrowException(v8_str("g"));
-}
-
-
-static void ThrowingSetAccessor(Local<String> name,
- Local<Value> value,
- const AccessorInfo& info) {
- v8::ThrowException(value);
-}
-
-
-THREADED_TEST(Regress1054726) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("x"),
- ThrowingGetAccessor,
- ThrowingSetAccessor,
- Local<Value>());
-
- LocalContext env;
- env->Global()->Set(v8_str("obj"), obj->NewInstance());
-
- // Use the throwing property setter/getter in a loop to force
- // the accessor ICs to be initialized.
- v8::Handle<Value> result;
- result = Script::Compile(v8_str(
- "var result = '';"
- "for (var i = 0; i < 5; i++) {"
- " try { obj.x; } catch (e) { result += e; }"
- "}; result"))->Run();
- CHECK_EQ(v8_str("ggggg"), result);
-
- result = Script::Compile(String::New(
- "var result = '';"
- "for (var i = 0; i < 5; i++) {"
- " try { obj.x = i; } catch (e) { result += e; }"
- "}; result"))->Run();
- CHECK_EQ(v8_str("01234"), result);
-}
-
-
-static v8::Handle<Value> AllocGetter(Local<String> name,
- const AccessorInfo& info) {
- ApiTestFuzzer::Fuzz();
- return v8::Array::New(1000);
-}
-
-
-THREADED_TEST(Gc) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- obj->SetAccessor(v8_str("xxx"), AllocGetter);
- LocalContext env;
- env->Global()->Set(v8_str("obj"), obj->NewInstance());
- Script::Compile(String::New(
- "var last = [];"
- "for (var i = 0; i < 2048; i++) {"
- " var result = obj.xxx;"
- " result[0] = last;"
- " last = result;"
- "}"))->Run();
-}
-
-
-static v8::Handle<Value> StackCheck(Local<String> name,
- const AccessorInfo& info) {
- i::StackFrameIterator iter;
- for (int i = 0; !iter.done(); i++) {
- i::StackFrame* frame = iter.frame();
- CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
- CHECK(frame->code()->IsCode());
- i::Address pc = frame->pc();
- i::Code* code = frame->code();
- CHECK(code->contains(pc));
- iter.Advance();
- }
- return v8::Undefined();
-}
-
-
-THREADED_TEST(StackIteration) {
- v8::HandleScope scope;
- v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
- i::StringStream::ClearMentionedObjectCache();
- obj->SetAccessor(v8_str("xxx"), StackCheck);
- LocalContext env;
- env->Global()->Set(v8_str("obj"), obj->NewInstance());
- Script::Compile(String::New(
- "function foo() {"
- " return obj.xxx;"
- "}"
- "for (var i = 0; i < 100; i++) {"
- " foo();"
- "}"))->Run();
-}
#include "utils.h"
#include "cctest.h"
-static const bool kLogThreading = false;
-
static bool IsNaN(double x) {
#ifdef WIN32
return _isnan(x);
namespace i = ::v8::internal;
+static Local<Value> v8_num(double x) {
+ return v8::Number::New(x);
+}
+
+
+static Local<String> v8_str(const char* x) {
+ return String::New(x);
+}
+
+
+static Local<Script> v8_compile(const char* x) {
+ return Script::Compile(v8_str(x));
+}
+
+
+// A LocalContext holds a reference to a v8::Context.
+class LocalContext {
+ public:
+ LocalContext(v8::ExtensionConfiguration* extensions = 0,
+ v8::Handle<ObjectTemplate> global_template =
+ v8::Handle<ObjectTemplate>(),
+ v8::Handle<Value> global_object = v8::Handle<Value>())
+ : context_(Context::New(extensions, global_template, global_object)) {
+ context_->Enter();
+ }
+
+ virtual ~LocalContext() {
+ context_->Exit();
+ context_.Dispose();
+ }
+
+ Context* operator->() { return *context_; }
+ Context* operator*() { return *context_; }
+ Local<Context> local() { return Local<Context>::New(context_); }
+ bool IsReady() { return !context_.IsEmpty(); }
+
+ private:
+ v8::Persistent<Context> context_;
+};
+
+
+// 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); \
+ /* */ TEST(Name)
+
+
+class RegisterThreadedTest {
+ public:
+ explicit RegisterThreadedTest(CcTest::TestFunction* callback)
+ : fuzzer_(NULL), callback_(callback) {
+ 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_;
+
+ private:
+ static RegisterThreadedTest* first_;
+ static int count_;
+ CcTest::TestFunction* callback_;
+ RegisterThreadedTest* prev_;
+};
+
+
+RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
+int RegisterThreadedTest::count_ = 0;
+
static int signature_callback_count;
static v8::Handle<Value> IncrementingSignatureCallback(
}
+// Helper function that compiles and runs the source.
+static Local<Value> CompileRun(const char* source) {
+ return Script::Compile(String::New(source))->Run();
+}
+
THREADED_TEST(ReceiverSignature) {
v8::HandleScope scope;
LocalContext env;
}
+static v8::Handle<Value> handle_property(Local<String> name,
+ const AccessorInfo&) {
+ ApiTestFuzzer::Fuzz();
+ return v8_num(900);
+}
+
+
+THREADED_TEST(PropertyHandler) {
+ v8::HandleScope scope;
+ Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
+ LocalContext env;
+ Local<Function> fun = fun_templ->GetFunction();
+ env->Global()->Set(v8_str("Fun"), fun);
+ Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
+ CHECK_EQ(900, getter->Run()->Int32Value());
+ Local<Script> setter = v8_compile("obj.foo = 901;");
+ CHECK_EQ(901, setter->Run()->Int32Value());
+}
+
+
THREADED_TEST(TinyInteger) {
v8::HandleScope scope;
LocalContext env;
}
+static v8::Handle<Value> GetIntValue(Local<String> property,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ int* value =
+ static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
+ return v8_num(*value);
+}
+
+static void SetIntValue(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ int* field =
+ static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
+ *field = value->Int32Value();
+}
+
+int foo, bar, baz;
+
+THREADED_TEST(GlobalVariableAccess) {
+ foo = 0;
+ bar = -4;
+ baz = 10;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
+ templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
+ GetIntValue,
+ SetIntValue,
+ v8::External::New(&foo));
+ templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
+ GetIntValue,
+ SetIntValue,
+ v8::External::New(&bar));
+ templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
+ GetIntValue,
+ SetIntValue,
+ v8::External::New(&baz));
+ LocalContext env(0, templ->InstanceTemplate());
+ v8_compile("foo = (++bar) + baz")->Run();
+ CHECK_EQ(bar, -3);
+ CHECK_EQ(foo, 7);
+}
+
+
THREADED_TEST(ObjectTemplate) {
v8::HandleScope scope;
Local<ObjectTemplate> templ1 = ObjectTemplate::New();
}
+static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ return v8::ThrowException(v8_str("g"));
+}
+
+
+static void ThrowingSetAccessor(Local<String> name,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ v8::ThrowException(value);
+}
+
+
+THREADED_TEST(Regress1054726) {
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
+ obj->SetAccessor(v8_str("x"),
+ ThrowingGetAccessor,
+ ThrowingSetAccessor,
+ Local<Value>());
+
+ LocalContext env;
+ env->Global()->Set(v8_str("obj"), obj->NewInstance());
+
+ // Use the throwing property setter/getter in a loop to force
+ // the accessor ICs to be initialized.
+ v8::Handle<Value> result;
+ result = Script::Compile(v8_str(
+ "var result = '';"
+ "for (var i = 0; i < 5; i++) {"
+ " try { obj.x; } catch (e) { result += e; }"
+ "}; result"))->Run();
+ CHECK_EQ(v8_str("ggggg"), result);
+
+ result = Script::Compile(String::New(
+ "var result = '';"
+ "for (var i = 0; i < 5; i++) {"
+ " try { obj.x = i; } catch (e) { result += e; }"
+ "}; result"))->Run();
+ CHECK_EQ(v8_str("01234"), result);
+}
+
+
THREADED_TEST(FunctionPrototype) {
v8::HandleScope scope;
Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
}
+static int x_register = 0;
+static v8::Handle<v8::Object> x_receiver;
+static v8::Handle<v8::Object> x_holder;
+
+
+static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ CHECK_EQ(x_receiver, info.This());
+ CHECK_EQ(x_holder, info.Holder());
+ return v8_num(x_register);
+}
+
+
+static void XSetter(Local<String> name,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ CHECK_EQ(x_holder, info.This());
+ CHECK_EQ(x_holder, info.Holder());
+ x_register = value->Int32Value();
+}
+
+
+THREADED_TEST(AccessorIC) {
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
+ obj->SetAccessor(v8_str("x"), XGetter, XSetter);
+ LocalContext context;
+ x_holder = obj->NewInstance();
+ context->Global()->Set(v8_str("holder"), x_holder);
+ x_receiver = v8::Object::New();
+ context->Global()->Set(v8_str("obj"), x_receiver);
+ v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
+ "obj.__proto__ = holder;"
+ "var result = [];"
+ "for (var i = 0; i < 10; i++) {"
+ " holder.x = i;"
+ " result.push(obj.x);"
+ "}"
+ "result"));
+ CHECK_EQ(10, array->Length());
+ for (int i = 0; i < 10; i++) {
+ v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
+ CHECK_EQ(v8::Integer::New(i), entry);
+ }
+}
+
+
static v8::Handle<Value> NoBlockGetterX(Local<String> name,
const AccessorInfo&) {
return v8::Handle<Value>();
// not start immediately.
bool ApiTestFuzzer::NextThread() {
int test_position = GetNextTestNumber();
- const char* test_name = RegisterThreadedTest::nth(current_)->name();
+ int test_number = RegisterThreadedTest::nth(current_)->fuzzer_->test_number_;
if (test_position == current_) {
- if (kLogThreading)
- printf("Stay with %s\n", test_name);
+ printf("Stay with %d\n", test_number);
return false;
}
- if (kLogThreading) {
- printf("Switch from %s to %s\n",
- test_name,
- RegisterThreadedTest::nth(test_position)->name());
- }
+ printf("Switch from %d to %d\n",
+ current_ < 0 ? 0 : test_number, test_position < 0 ? 0 : test_number);
current_ = test_position;
RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
return true;
void ApiTestFuzzer::CallTest() {
- if (kLogThreading)
- printf("Start test %d\n", test_number_);
+ printf("Start test %d\n", test_number_);
CallTestNumber(test_number_);
- if (kLogThreading)
- printf("End test %d\n", test_number_);
+ printf("End test %d\n", test_number_);
}
}
+static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
+ Local<String> name,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ return v8::True();
+}
+
+
+THREADED_TEST(AccessorProhibitsOverwriting) {
+ v8::HandleScope scope;
+ LocalContext context;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessor(v8_str("x"),
+ AccessorProhibitsOverwritingGetter,
+ 0,
+ v8::Handle<Value>(),
+ v8::PROHIBITS_OVERWRITING,
+ v8::ReadOnly);
+ Local<v8::Object> instance = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), instance);
+ Local<Value> value = CompileRun(
+ "obj.__defineGetter__('x', function() { return false; });"
+ "obj.x");
+ CHECK(value->BooleanValue());
+ value = CompileRun(
+ "var setter_called = false;"
+ "obj.__defineSetter__('x', function() { setter_called = true; });"
+ "obj.x = 42;"
+ "setter_called");
+ CHECK(!value->BooleanValue());
+ value = CompileRun(
+ "obj2 = {};"
+ "obj2.__proto__ = obj;"
+ "obj2.__defineGetter__('x', function() { return false; });"
+ "obj2.x");
+ CHECK(value->BooleanValue());
+ value = CompileRun(
+ "var setter_called = false;"
+ "obj2 = {};"
+ "obj2.__proto__ = obj;"
+ "obj2.__defineSetter__('x', function() { setter_called = true; });"
+ "obj2.x = 42;"
+ "setter_called");
+ CHECK(!value->BooleanValue());
+}
+
+
static bool NamedSetAccessBlocker(Local<v8::Object> obj,
Local<Value> name,
v8::AccessType type,
}
+// Helper function that compiles and runs the source.
+static v8::Local<v8::Value> CompileRun(const char* source) {
+ return v8::Script::Compile(v8::String::New(source))->Run();
+}
+
+
// Is there any debug info for the function?
static bool HasDebugInfo(v8::Handle<v8::Function> fun) {
Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun);
}
+static void CompileRun(const char* source) {
+ Script::Compile(String::New(source))->Run();
+}
+
+
v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2(
const v8::Arguments& args) {
v8::HandleScope scope;
"Log": true,
"DeclareGlobals": true,
- "CollectStackTrace": true,
- "PromoteScheduledException": true,
- "DeleteHandleScopeExtensions": true
+ "CollectStackTrace": true
};
var currentlyUncallable = {