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:
parent
9445079c51
commit
53b93464d1
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) { }
|
||||
|
@ -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;
|
||||
|
23
src/frames.h
23
src/frames.h
@ -93,7 +93,6 @@ class StackHandler BASE_EMBEDDED {
|
||||
V(ENTRY, EntryFrame) \
|
||||
V(ENTRY_CONSTRUCT, EntryConstructFrame) \
|
||||
V(EXIT, ExitFrame) \
|
||||
V(EXIT_DEBUG, ExitDebugFrame) \
|
||||
V(JAVA_SCRIPT, JavaScriptFrame) \
|
||||
V(INTERNAL, InternalFrame) \
|
||||
V(CONSTRUCT, ConstructFrame) \
|
||||
@ -119,7 +118,6 @@ class StackFrame BASE_EMBEDDED {
|
||||
bool is_entry() const { return type() == ENTRY; }
|
||||
bool is_entry_construct() const { return type() == ENTRY_CONSTRUCT; }
|
||||
bool is_exit() const { return type() == EXIT; }
|
||||
bool is_exit_debug() const { return type() == EXIT_DEBUG; }
|
||||
bool is_java_script() const { return type() == JAVA_SCRIPT; }
|
||||
bool is_arguments_adaptor() const { return type() == ARGUMENTS_ADAPTOR; }
|
||||
bool is_internal() const { return type() == INTERNAL; }
|
||||
@ -260,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.
|
||||
|
@ -105,6 +105,21 @@ void HandleScope::ZapRange(Object** start, Object** end) {
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -979,6 +979,7 @@ void AccessorInfo::AccessorInfoVerify() {
|
||||
VerifyPointer(name());
|
||||
VerifyPointer(data());
|
||||
VerifyPointer(flag());
|
||||
VerifyPointer(load_stub_cache());
|
||||
}
|
||||
|
||||
void AccessorInfo::AccessorInfoPrint() {
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
{
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -34,6 +34,7 @@ Import('context object_files')
|
||||
|
||||
SOURCES = {
|
||||
'all': [
|
||||
'test-accessors.cc',
|
||||
'test-alloc.cc',
|
||||
'test-api.cc',
|
||||
'test-ast.cc',
|
||||
|
@ -121,3 +121,6 @@ int main(int argc, char* argv[]) {
|
||||
v8::V8::Dispose();
|
||||
return 0;
|
||||
}
|
||||
|
||||
RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
|
||||
int RegisterThreadedTest::count_ = 0;
|
||||
|
@ -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_
|
||||
|
424
test/cctest/test-accessors.cc
Normal file
424
test/cctest/test-accessors.cc
Normal 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();
|
||||
}
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -129,7 +129,9 @@ var knownProblems = {
|
||||
"Log": true,
|
||||
"DeclareGlobals": true,
|
||||
|
||||
"CollectStackTrace": true
|
||||
"CollectStackTrace": true,
|
||||
"PromoteScheduledException": true,
|
||||
"DeleteHandleScopeExtensions": true
|
||||
};
|
||||
|
||||
var currentlyUncallable = {
|
||||
|
Loading…
Reference in New Issue
Block a user