Implemented specialized stubs for API getters. This includes a number

of individual changes:

  - Added infrastructure for custom stub caching.
  - Push the code object onto the stack in exit calls instead of a
    debug/non-debug marker.
  - Remove the DEBUG_EXIT frame type.
  - Add a new exit stub generator for API getters.

Review URL: http://codereview.chromium.org/330017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3130 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
christian.plesner.hansen@gmail.com 2009-10-26 13:54:47 +00:00
parent 9445079c51
commit 53b93464d1
42 changed files with 1012 additions and 499 deletions

View File

@ -125,6 +125,15 @@ static inline v8::internal::Handle<v8::internal::Object> 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<v8::Value> data,
v8::Local<v8::Object> holder,
v8::Local<v8::Function> callee,

View File

@ -5797,7 +5797,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
@ -5857,7 +5857,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;
@ -5903,12 +5903,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)
@ -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,
false,
false);
@ -5932,7 +5932,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);
@ -5943,7 +5943,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);

View File

@ -54,18 +54,16 @@ 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<Address*>(sp - 1 * kPointerSize);
return type;
return EXIT;
}

View File

@ -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;

View File

@ -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(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);
}

View File

@ -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);

View File

@ -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() {

View File

@ -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>(address_);}
#ifdef ENABLE_DEBUGGER_SUPPORT

View File

@ -36,10 +36,27 @@ namespace v8 {
namespace internal {
Handle<Code> 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<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;
// Update the static counter each time a new code stub is generated.
Counters::code_stubs.Increment();
@ -79,18 +96,21 @@ Handle<Code> CodeStub::GetCode() {
}
#endif
// 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);
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;
}
ASSERT(index != NumberDictionary::kNotFound);
return Handle<Code>(Code::cast(Heap::code_stubs()->ValueAt(index)));
return Handle<Code>(result);
}

View File

@ -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;

View File

@ -551,4 +551,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

View File

@ -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<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) { }

View File

@ -394,7 +394,13 @@ Code* EntryConstructFrame::code() const {
Code* ExitFrame::code() const {
return Heap::c_entry_code();
const int offset = ExitFrameConstants::kCodeOffset;
Object* code = Memory::Object_at(fp() + offset);
if (code->IsSmi()) {
return Heap::c_entry_debug_break_code();
} else {
return Code::cast(code);
}
}
@ -412,11 +418,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;

View File

@ -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,6 +258,7 @@ 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;
@ -289,26 +288,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<ExitDebugFrame*>(frame);
}
protected:
explicit ExitDebugFrame(StackFrameIterator* iterator)
: ExitFrame(iterator) { }
private:
friend class StackFrameIterator;
};
class StandardFrame: public StackFrame {
public:
// Testers.

View File

@ -105,6 +105,21 @@ void HandleScope::ZapRange(Object** start, Object** end) {
}
Address HandleScope::current_extensions_address() {
return reinterpret_cast<Address>(&current_.extensions);
}
Address HandleScope::current_next_address() {
return reinterpret_cast<Address>(&current_.next);
}
Address HandleScope::current_limit_address() {
return reinterpret_cast<Address>(&current_.limit);
}
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
Handle<JSArray> array) {
CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray);

View File

@ -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);

View File

@ -7699,11 +7699,66 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
}
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
Label get_result;
Label prologue;
Label promote_scheduled_exception;
__ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc);
ASSERT_EQ(kArgc, 4);
// 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.
__ mov(eax, Immediate(ExternalReference(fun())));
// Call the api function!
__ call(Operand(eax));
// 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);
// The returned value is a pointer to the handle holding the result.
// Dereference this to get to the handle.
__ 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
@ -7753,7 +7808,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.
@ -7852,12 +7907,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)
@ -7875,7 +7930,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);
@ -7884,7 +7939,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);
@ -7895,7 +7950,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);

View File

@ -56,12 +56,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
// Determine frame type.
if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
return EXIT_DEBUG;
} else {
return EXIT;
}
return EXIT;
}

View File

@ -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;

View File

@ -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,47 @@ void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
}
void MacroAssembler::PushHandleScope(Register scratch) {
// Push the number of extensions, smi-tagged for make benefit the gc.
ExternalReference extensions_address =
ExternalReference::handle_scope_extensions_address();
mov(scratch, Operand::StaticVariable(extensions_address));
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));

View File

@ -81,12 +81,14 @@ class MacroAssembler: public Assembler {
// 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(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,

View File

@ -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<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(receiver); // receiver
__ push(reg); // holder
__ mov(reg, Immediate(Handle<AccessorInfo>(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 tail-call to the runtime system.
ExternalReference load_callback_property =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
__ TailCallRuntime(load_callback_property, 5, 1);
// 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);
Register tmp = other.is(eax) ? reg : other;
__ PopHandleScope(tmp);
__ LeaveInternalFrame();
__ ret(0);
}

View File

@ -979,6 +979,7 @@ void AccessorInfo::AccessorInfoVerify() {
VerifyPointer(name());
VerifyPointer(data());
VerifyPointer(flag());
VerifyPointer(load_stub_cache());
}
void AccessorInfo::AccessorInfoPrint() {

View File

@ -2417,6 +2417,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)

View File

@ -4708,6 +4708,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);
@ -4733,7 +4734,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.

View File

@ -4797,6 +4797,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);
@ -7758,6 +7764,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.

View File

@ -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)

View File

@ -735,11 +735,16 @@ Handle<Code> 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<Address>(callback->getter());
v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(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<v8::Value> result;
{

View File

@ -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_;

View File

@ -6784,7 +6784,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.
@ -6865,7 +6865,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.
@ -6995,12 +6995,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
@ -7023,7 +7023,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);
@ -7032,7 +7032,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);
@ -7043,7 +7043,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);
@ -7058,6 +7058,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

View File

@ -57,11 +57,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
// ---------------------------------------------------------------------------

View File

@ -34,6 +34,7 @@ Import('context object_files')
SOURCES = {
'all': [
'test-accessors.cc',
'test-alloc.cc',
'test-api.cc',
'test-ast.cc',

View File

@ -121,3 +121,6 @@ int main(int argc, char* argv[]) {
v8::V8::Dispose();
return 0;
}
RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
int RegisterThreadedTest::count_ = 0;

View File

@ -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<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_

View File

@ -0,0 +1,424 @@
// 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();
ASSERT(i != 0 || (frame->type() == i::StackFrame::EXIT));
ASSERT(frame->code()->IsCode());
i::Address pc = frame->pc();
i::Code* code = frame->code();
ASSERT(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();
}

View File

@ -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<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(
@ -231,11 +108,6 @@ THREADED_TEST(Handles) {
}
// 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;
@ -720,27 +592,6 @@ THREADED_TEST(FindInstanceInPrototypeChain) {
}
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;
@ -907,49 +758,6 @@ THREADED_TEST(GlobalPrototype) {
}
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();
@ -1365,50 +1173,6 @@ THREADED_TEST(CallbackExceptionRegression) {
}
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();
@ -3184,53 +2948,6 @@ THREADED_TEST(Arguments) {
}
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>();
@ -6094,13 +5811,17 @@ void ApiTestFuzzer::Fuzz() {
// not start immediately.
bool ApiTestFuzzer::NextThread() {
int test_position = GetNextTestNumber();
int test_number = RegisterThreadedTest::nth(current_)->fuzzer_->test_number_;
const char* test_name = RegisterThreadedTest::nth(current_)->name();
if (test_position == current_) {
printf("Stay with %d\n", test_number);
if (kLogThreading)
printf("Stay with %s\n", test_name);
return false;
}
printf("Switch from %d to %d\n",
current_ < 0 ? 0 : test_number, test_position < 0 ? 0 : test_number);
if (kLogThreading) {
printf("Switch from %s to %s\n",
test_name,
RegisterThreadedTest::nth(test_position)->name());
}
current_ = test_position;
RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
return true;
@ -6209,9 +5930,11 @@ TEST(Threading2) {
void ApiTestFuzzer::CallTest() {
printf("Start test %d\n", test_number_);
if (kLogThreading)
printf("Start test %d\n", test_number_);
CallTestNumber(test_number_);
printf("End test %d\n", test_number_);
if (kLogThreading)
printf("End test %d\n", test_number_);
}
@ -6699,53 +6422,6 @@ THREADED_TEST(PropertyEnumeration) {
}
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,

View File

@ -178,12 +178,6 @@ static v8::Local<v8::Function> CompileFunction(const char* source,
}
// 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);

View File

@ -163,11 +163,6 @@ v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) {
}
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;

View File

@ -129,7 +129,9 @@ var knownProblems = {
"Log": true,
"DeclareGlobals": true,
"CollectStackTrace": true
"CollectStackTrace": true,
"PromoteScheduledException": true,
"DeleteHandleScopeExtensions": true
};
var currentlyUncallable = {