Reverting 3174. Aka reapplying 3150, 3151 and 3159. Aka api accessor
ics. Review URL: http://codereview.chromium.org/341082 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3209 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
b9d37517d2
commit
a1b2f47600
@ -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,
|
||||
|
@ -5777,7 +5777,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
Label* throw_normal_exception,
|
||||
Label* throw_termination_exception,
|
||||
Label* throw_out_of_memory_exception,
|
||||
StackFrame::Type frame_type,
|
||||
ExitFrame::Mode mode,
|
||||
bool do_gc,
|
||||
bool always_allocate) {
|
||||
// r0: result parameter for PerformGC, if any
|
||||
@ -5837,7 +5837,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
// r0:r1: result
|
||||
// sp: stack pointer
|
||||
// fp: frame pointer
|
||||
__ LeaveExitFrame(frame_type);
|
||||
__ LeaveExitFrame(mode);
|
||||
|
||||
// check if we should retry or throw exception
|
||||
Label retry;
|
||||
@ -5883,12 +5883,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
// this by performing a garbage collection and retrying the
|
||||
// builtin once.
|
||||
|
||||
StackFrame::Type frame_type = is_debug_break
|
||||
? StackFrame::EXIT_DEBUG
|
||||
: StackFrame::EXIT;
|
||||
ExitFrame::Mode mode = is_debug_break
|
||||
? ExitFrame::MODE_DEBUG
|
||||
: ExitFrame::MODE_NORMAL;
|
||||
|
||||
// Enter the exit frame that transitions from JavaScript to C++.
|
||||
__ EnterExitFrame(frame_type);
|
||||
__ EnterExitFrame(mode);
|
||||
|
||||
// r4: number of arguments (C callee-saved)
|
||||
// r5: pointer to builtin function (C callee-saved)
|
||||
@ -5903,7 +5903,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
false,
|
||||
false);
|
||||
|
||||
@ -5912,7 +5912,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
false);
|
||||
|
||||
@ -5923,7 +5923,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
true);
|
||||
|
||||
|
@ -54,23 +54,24 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
|
||||
if (fp == 0) return NONE;
|
||||
// Compute frame type and stack pointer.
|
||||
Address sp = fp + ExitFrameConstants::kSPDisplacement;
|
||||
Type type;
|
||||
if (Memory::Address_at(fp + ExitFrameConstants::kDebugMarkOffset) != 0) {
|
||||
type = EXIT_DEBUG;
|
||||
const int offset = ExitFrameConstants::kCodeOffset;
|
||||
Object* code = Memory::Object_at(fp + offset);
|
||||
bool is_debug_exit = code->IsSmi();
|
||||
if (is_debug_exit) {
|
||||
sp -= kNumJSCallerSaved * kPointerSize;
|
||||
} else {
|
||||
type = EXIT;
|
||||
}
|
||||
// Fill in the state.
|
||||
state->sp = sp;
|
||||
state->fp = fp;
|
||||
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
|
||||
return type;
|
||||
return EXIT;
|
||||
}
|
||||
|
||||
|
||||
void ExitFrame::Iterate(ObjectVisitor* v) const {
|
||||
// Do nothing
|
||||
v->VisitPointer(&code_slot());
|
||||
// The arguments are traversed as part of the expression stack of
|
||||
// the calling frame.
|
||||
}
|
||||
|
||||
|
||||
|
@ -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(Smi::FromInt(0)));
|
||||
} else {
|
||||
mov(ip, Operand(CodeObject()));
|
||||
}
|
||||
push(ip);
|
||||
|
||||
// Save the frame pointer and the context in top.
|
||||
@ -316,7 +317,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Save the state of all registers to the stack from the memory
|
||||
// location. This is needed to allow nested break points.
|
||||
if (type == StackFrame::EXIT_DEBUG) {
|
||||
if (mode == ExitFrame::MODE_DEBUG) {
|
||||
// Use sp as base to push.
|
||||
CopyRegistersFromMemoryToStack(sp, kJSCallerSaved);
|
||||
}
|
||||
@ -348,14 +349,14 @@ void MacroAssembler::AlignStack(int offset) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
|
||||
void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Restore the memory copy of the registers by digging them out from
|
||||
// the stack. This is needed to allow nested break points.
|
||||
if (type == StackFrame::EXIT_DEBUG) {
|
||||
if (mode == ExitFrame::MODE_DEBUG) {
|
||||
// This code intentionally clobbers r2 and r3.
|
||||
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
|
||||
const int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
|
||||
const int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
|
||||
add(r3, fp, Operand(kOffset));
|
||||
CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -552,4 +552,20 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
bool ApiGetterEntryStub::GetCustomCache(Code** code_out) {
|
||||
Object* cache = info()->load_stub_cache();
|
||||
if (cache->IsUndefined()) {
|
||||
return false;
|
||||
} else {
|
||||
*code_out = Code::cast(cache);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ApiGetterEntryStub::SetCustomCache(Code* value) {
|
||||
info()->set_load_stub_cache(value);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -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) { }
|
||||
|
@ -393,8 +393,19 @@ Code* EntryConstructFrame::code() const {
|
||||
}
|
||||
|
||||
|
||||
Object*& ExitFrame::code_slot() const {
|
||||
const int offset = ExitFrameConstants::kCodeOffset;
|
||||
return Memory::Object_at(fp() + offset);
|
||||
}
|
||||
|
||||
|
||||
Code* ExitFrame::code() const {
|
||||
return Heap::c_entry_code();
|
||||
Object* code = code_slot();
|
||||
if (code->IsSmi()) {
|
||||
return Heap::c_entry_debug_break_code();
|
||||
} else {
|
||||
return Code::cast(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -412,11 +423,6 @@ Address ExitFrame::GetCallerStackPointer() const {
|
||||
}
|
||||
|
||||
|
||||
Code* ExitDebugFrame::code() const {
|
||||
return Heap::c_entry_debug_break_code();
|
||||
}
|
||||
|
||||
|
||||
Address StandardFrame::GetExpressionAddress(int n) const {
|
||||
const int offset = StandardFrameConstants::kExpressionsOffset;
|
||||
return fp() + offset - n * kPointerSize;
|
||||
|
25
src/frames.h
25
src/frames.h
@ -93,7 +93,6 @@ class StackHandler BASE_EMBEDDED {
|
||||
V(ENTRY, EntryFrame) \
|
||||
V(ENTRY_CONSTRUCT, EntryConstructFrame) \
|
||||
V(EXIT, ExitFrame) \
|
||||
V(EXIT_DEBUG, ExitDebugFrame) \
|
||||
V(JAVA_SCRIPT, JavaScriptFrame) \
|
||||
V(INTERNAL, InternalFrame) \
|
||||
V(CONSTRUCT, ConstructFrame) \
|
||||
@ -119,7 +118,6 @@ class StackFrame BASE_EMBEDDED {
|
||||
bool is_entry() const { return type() == ENTRY; }
|
||||
bool is_entry_construct() const { return type() == ENTRY_CONSTRUCT; }
|
||||
bool is_exit() const { return type() == EXIT; }
|
||||
bool is_exit_debug() const { return type() == EXIT_DEBUG; }
|
||||
bool is_java_script() const { return type() == JAVA_SCRIPT; }
|
||||
bool is_arguments_adaptor() const { return type() == ARGUMENTS_ADAPTOR; }
|
||||
bool is_internal() const { return type() == INTERNAL; }
|
||||
@ -260,10 +258,13 @@ class EntryConstructFrame: public EntryFrame {
|
||||
// Exit frames are used to exit JavaScript execution and go to C.
|
||||
class ExitFrame: public StackFrame {
|
||||
public:
|
||||
enum Mode { MODE_NORMAL, MODE_DEBUG };
|
||||
virtual Type type() const { return EXIT; }
|
||||
|
||||
virtual Code* code() const;
|
||||
|
||||
Object*& code_slot() const;
|
||||
|
||||
// Garbage collection support.
|
||||
virtual void Iterate(ObjectVisitor* v) const;
|
||||
|
||||
@ -289,26 +290,6 @@ class ExitFrame: public StackFrame {
|
||||
};
|
||||
|
||||
|
||||
class ExitDebugFrame: public ExitFrame {
|
||||
public:
|
||||
virtual Type type() const { return EXIT_DEBUG; }
|
||||
|
||||
virtual Code* code() const;
|
||||
|
||||
static ExitDebugFrame* cast(StackFrame* frame) {
|
||||
ASSERT(frame->is_exit_debug());
|
||||
return static_cast<ExitDebugFrame*>(frame);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ExitDebugFrame(StackFrameIterator* iterator)
|
||||
: ExitFrame(iterator) { }
|
||||
|
||||
private:
|
||||
friend class StackFrameIterator;
|
||||
};
|
||||
|
||||
|
||||
class StandardFrame: public StackFrame {
|
||||
public:
|
||||
// Testers.
|
||||
|
@ -103,6 +103,10 @@ typedef byte* Address;
|
||||
#define V8PRIxPTR "lx"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define USING_MAC_ABI
|
||||
#endif
|
||||
|
||||
// Code-point values in Unicode 4.0 are 21 bits wide.
|
||||
typedef uint16_t uc16;
|
||||
typedef int32_t uc32;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -7686,11 +7686,84 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
// If true, a Handle<T> passed by value is passed and returned by
|
||||
// using the location_ field directly. If false, it is passed and
|
||||
// returned as a pointer to a handle.
|
||||
#ifdef USING_MAC_ABI
|
||||
static const bool kPassHandlesDirectly = true;
|
||||
#else
|
||||
static const bool kPassHandlesDirectly = false;
|
||||
#endif
|
||||
|
||||
|
||||
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
|
||||
Label get_result;
|
||||
Label prologue;
|
||||
Label promote_scheduled_exception;
|
||||
__ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc);
|
||||
ASSERT_EQ(kArgc, 4);
|
||||
if (kPassHandlesDirectly) {
|
||||
// When handles as passed directly we don't have to allocate extra
|
||||
// space for and pass an out parameter.
|
||||
__ mov(Operand(esp, 0 * kPointerSize), ebx); // name.
|
||||
__ mov(Operand(esp, 1 * kPointerSize), eax); // arguments pointer.
|
||||
} else {
|
||||
// The function expects three arguments to be passed but we allocate
|
||||
// four to get space for the output cell. The argument slots are filled
|
||||
// as follows:
|
||||
//
|
||||
// 3: output cell
|
||||
// 2: arguments pointer
|
||||
// 1: name
|
||||
// 0: pointer to the output cell
|
||||
//
|
||||
// Note that this is one more "argument" than the function expects
|
||||
// so the out cell will have to be popped explicitly after returning
|
||||
// from the function.
|
||||
__ mov(Operand(esp, 1 * kPointerSize), ebx); // name.
|
||||
__ mov(Operand(esp, 2 * kPointerSize), eax); // arguments pointer.
|
||||
__ mov(ebx, esp);
|
||||
__ add(Operand(ebx), Immediate(3 * kPointerSize));
|
||||
__ mov(Operand(esp, 0 * kPointerSize), ebx); // output
|
||||
__ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); // out cell.
|
||||
}
|
||||
// Call the api function!
|
||||
__ call(fun()->address(), RelocInfo::RUNTIME_ENTRY);
|
||||
// Check if the function scheduled an exception.
|
||||
ExternalReference scheduled_exception_address =
|
||||
ExternalReference::scheduled_exception_address();
|
||||
__ cmp(Operand::StaticVariable(scheduled_exception_address),
|
||||
Immediate(Factory::the_hole_value()));
|
||||
__ j(not_equal, &promote_scheduled_exception, not_taken);
|
||||
if (!kPassHandlesDirectly) {
|
||||
// The returned value is a pointer to the handle holding the result.
|
||||
// Dereference this to get to the location.
|
||||
__ mov(eax, Operand(eax, 0));
|
||||
}
|
||||
// Check if the result handle holds 0
|
||||
__ test(eax, Operand(eax));
|
||||
__ j(not_zero, &get_result, taken);
|
||||
// It was zero; the result is undefined.
|
||||
__ mov(eax, Factory::undefined_value());
|
||||
__ jmp(&prologue);
|
||||
// It was non-zero. Dereference to get the result value.
|
||||
__ bind(&get_result);
|
||||
__ mov(eax, Operand(eax, 0));
|
||||
__ bind(&prologue);
|
||||
__ LeaveExitFrame(ExitFrame::MODE_NORMAL);
|
||||
__ ret(0);
|
||||
__ bind(&promote_scheduled_exception);
|
||||
__ TailCallRuntime(ExternalReference(Runtime::kPromoteScheduledException),
|
||||
0,
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
Label* throw_normal_exception,
|
||||
Label* throw_termination_exception,
|
||||
Label* throw_out_of_memory_exception,
|
||||
StackFrame::Type frame_type,
|
||||
ExitFrame::Mode mode,
|
||||
bool do_gc,
|
||||
bool always_allocate_scope) {
|
||||
// eax: result parameter for PerformGC, if any
|
||||
@ -7740,7 +7813,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
__ j(zero, &failure_returned, not_taken);
|
||||
|
||||
// Exit the JavaScript to C++ exit frame.
|
||||
__ LeaveExitFrame(frame_type);
|
||||
__ LeaveExitFrame(mode);
|
||||
__ ret(0);
|
||||
|
||||
// Handling of failure.
|
||||
@ -7839,12 +7912,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
// of a proper result. The builtin entry handles this by performing
|
||||
// a garbage collection and retrying the builtin (twice).
|
||||
|
||||
StackFrame::Type frame_type = is_debug_break ?
|
||||
StackFrame::EXIT_DEBUG :
|
||||
StackFrame::EXIT;
|
||||
ExitFrame::Mode mode = is_debug_break
|
||||
? ExitFrame::MODE_DEBUG
|
||||
: ExitFrame::MODE_NORMAL;
|
||||
|
||||
// Enter the exit frame that transitions from JavaScript to C++.
|
||||
__ EnterExitFrame(frame_type);
|
||||
__ EnterExitFrame(mode);
|
||||
|
||||
// eax: result parameter for PerformGC, if any (setup below)
|
||||
// ebx: pointer to builtin function (C callee-saved)
|
||||
@ -7862,7 +7935,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
false,
|
||||
false);
|
||||
|
||||
@ -7871,7 +7944,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
false);
|
||||
|
||||
@ -7882,7 +7955,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
true);
|
||||
|
||||
|
@ -56,19 +56,14 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
void ExitFrame::Iterate(ObjectVisitor* v) const {
|
||||
// Exit frames on IA-32 do not contain any pointers. The arguments
|
||||
// are traversed as part of the expression stack of the calling
|
||||
// frame.
|
||||
v->VisitPointer(&code_slot());
|
||||
// The arguments are traversed as part of the expression stack of
|
||||
// the calling frame.
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,48 @@ void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushHandleScope(Register scratch) {
|
||||
// Push the number of extensions, smi-tagged so the gc will ignore it.
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
mov(scratch, Operand::StaticVariable(extensions_address));
|
||||
ASSERT_EQ(0, kSmiTag);
|
||||
shl(scratch, kSmiTagSize);
|
||||
push(scratch);
|
||||
mov(Operand::StaticVariable(extensions_address), Immediate(0));
|
||||
// Push next and limit pointers which will be wordsize aligned and
|
||||
// hence automatically smi tagged.
|
||||
ExternalReference next_address =
|
||||
ExternalReference::handle_scope_next_address();
|
||||
push(Operand::StaticVariable(next_address));
|
||||
ExternalReference limit_address =
|
||||
ExternalReference::handle_scope_limit_address();
|
||||
push(Operand::StaticVariable(limit_address));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopHandleScope(Register scratch) {
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
Label write_back;
|
||||
mov(scratch, Operand::StaticVariable(extensions_address));
|
||||
cmp(Operand(scratch), Immediate(0));
|
||||
j(equal, &write_back);
|
||||
CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
|
||||
|
||||
bind(&write_back);
|
||||
ExternalReference limit_address =
|
||||
ExternalReference::handle_scope_limit_address();
|
||||
pop(Operand::StaticVariable(limit_address));
|
||||
ExternalReference next_address =
|
||||
ExternalReference::handle_scope_next_address();
|
||||
pop(Operand::StaticVariable(next_address));
|
||||
pop(scratch);
|
||||
shr(scratch, kSmiTagSize);
|
||||
mov(Operand::StaticVariable(extensions_address), scratch);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::JumpToRuntime(const ExternalReference& ext) {
|
||||
// Set the entry point and jump to the C entry runtime stub.
|
||||
mov(ebx, Immediate(ext));
|
||||
|
@ -77,16 +77,18 @@ class MacroAssembler: public Assembler {
|
||||
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
|
||||
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
|
||||
|
||||
// Enter specific kind of exit frame; either EXIT or
|
||||
// EXIT_DEBUG. Expects the number of arguments in register eax and
|
||||
// Enter specific kind of exit frame; either in normal or debug mode.
|
||||
// Expects the number of arguments in register eax and
|
||||
// sets up the number of arguments in register edi and the pointer
|
||||
// to the first argument in register esi.
|
||||
void EnterExitFrame(StackFrame::Type type);
|
||||
void EnterExitFrame(ExitFrame::Mode mode);
|
||||
|
||||
void EnterApiExitFrame(ExitFrame::Mode mode, int stack_space, int argc);
|
||||
|
||||
// Leave the current exit frame. Expects the return value in
|
||||
// register eax:edx (untouched) and the pointer to the first
|
||||
// argument in register esi.
|
||||
void LeaveExitFrame(StackFrame::Type type);
|
||||
void LeaveExitFrame(ExitFrame::Mode mode);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -269,6 +271,9 @@ class MacroAssembler: public Assembler {
|
||||
int num_arguments,
|
||||
int result_size);
|
||||
|
||||
void PushHandleScope(Register scratch);
|
||||
void PopHandleScope(Register scratch);
|
||||
|
||||
// Jump to a runtime routine.
|
||||
void JumpToRuntime(const ExternalReference& ext);
|
||||
|
||||
@ -346,6 +351,9 @@ class MacroAssembler: public Assembler {
|
||||
void EnterFrame(StackFrame::Type type);
|
||||
void LeaveFrame(StackFrame::Type type);
|
||||
|
||||
void EnterExitFramePrologue(ExitFrame::Mode mode);
|
||||
void EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc);
|
||||
|
||||
// Allocation support helpers.
|
||||
void LoadAllocationTopHelper(Register result,
|
||||
Register result_end,
|
||||
|
@ -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() {
|
||||
|
@ -2436,6 +2436,7 @@ ACCESSORS(AccessorInfo, setter, Object, kSetterOffset)
|
||||
ACCESSORS(AccessorInfo, data, Object, kDataOffset)
|
||||
ACCESSORS(AccessorInfo, name, Object, kNameOffset)
|
||||
ACCESSORS(AccessorInfo, flag, Smi, kFlagOffset)
|
||||
ACCESSORS(AccessorInfo, load_stub_cache, Object, kLoadStubCacheOffset)
|
||||
|
||||
ACCESSORS(AccessCheckInfo, named_callback, Object, kNamedCallbackOffset)
|
||||
ACCESSORS(AccessCheckInfo, indexed_callback, Object, kIndexedCallbackOffset)
|
||||
|
@ -4724,6 +4724,7 @@ class AccessorInfo: public Struct {
|
||||
DECL_ACCESSORS(data, Object)
|
||||
DECL_ACCESSORS(name, Object)
|
||||
DECL_ACCESSORS(flag, Smi)
|
||||
DECL_ACCESSORS(load_stub_cache, Object)
|
||||
|
||||
inline bool all_can_read();
|
||||
inline void set_all_can_read(bool value);
|
||||
@ -4749,7 +4750,8 @@ class AccessorInfo: public Struct {
|
||||
static const int kDataOffset = kSetterOffset + kPointerSize;
|
||||
static const int kNameOffset = kDataOffset + kPointerSize;
|
||||
static const int kFlagOffset = kNameOffset + kPointerSize;
|
||||
static const int kSize = kFlagOffset + kPointerSize;
|
||||
static const int kLoadStubCacheOffset = kFlagOffset + kPointerSize;
|
||||
static const int kSize = kLoadStubCacheOffset + kPointerSize;
|
||||
|
||||
private:
|
||||
// Bit positions in flag.
|
||||
|
@ -4831,6 +4831,12 @@ static Object* Runtime_ReThrow(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_PromoteScheduledException(Arguments args) {
|
||||
ASSERT_EQ(0, args.length());
|
||||
return Top::PromoteScheduledException();
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_ThrowReferenceError(Arguments args) {
|
||||
HandleScope scope;
|
||||
ASSERT(args.length() == 1);
|
||||
@ -7792,6 +7798,13 @@ static Object* Runtime_Abort(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
|
||||
ASSERT(args.length() == 0);
|
||||
HandleScope::DeleteExtensions();
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
// ListNatives is ONLY used by the fuzz-natives.js in debug mode
|
||||
// Exclude the code in release mode.
|
||||
|
@ -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_;
|
||||
|
@ -6750,7 +6750,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
Label* throw_normal_exception,
|
||||
Label* throw_termination_exception,
|
||||
Label* throw_out_of_memory_exception,
|
||||
StackFrame::Type frame_type,
|
||||
ExitFrame::Mode mode,
|
||||
bool do_gc,
|
||||
bool always_allocate_scope) {
|
||||
// rax: result parameter for PerformGC, if any.
|
||||
@ -6833,7 +6833,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
__ j(zero, &failure_returned);
|
||||
|
||||
// Exit the JavaScript to C++ exit frame.
|
||||
__ LeaveExitFrame(frame_type, result_size_);
|
||||
__ LeaveExitFrame(mode, result_size_);
|
||||
__ ret(0);
|
||||
|
||||
// Handling of failure.
|
||||
@ -6963,12 +6963,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
// this by performing a garbage collection and retrying the
|
||||
// builtin once.
|
||||
|
||||
StackFrame::Type frame_type = is_debug_break ?
|
||||
StackFrame::EXIT_DEBUG :
|
||||
StackFrame::EXIT;
|
||||
ExitFrame::Mode mode = is_debug_break ?
|
||||
ExitFrame::MODE_DEBUG :
|
||||
ExitFrame::MODE_NORMAL;
|
||||
|
||||
// Enter the exit frame that transitions from JavaScript to C++.
|
||||
__ EnterExitFrame(frame_type, result_size_);
|
||||
__ EnterExitFrame(mode, result_size_);
|
||||
|
||||
// rax: Holds the context at this point, but should not be used.
|
||||
// On entry to code generated by GenerateCore, it must hold
|
||||
@ -6991,7 +6991,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
false,
|
||||
false);
|
||||
|
||||
@ -7000,7 +7000,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
false);
|
||||
|
||||
@ -7011,7 +7011,7 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
&throw_normal_exception,
|
||||
&throw_termination_exception,
|
||||
&throw_out_of_memory_exception,
|
||||
frame_type,
|
||||
mode,
|
||||
true,
|
||||
true);
|
||||
|
||||
@ -7026,6 +7026,11 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
|
||||
}
|
||||
|
||||
|
||||
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
Label invoke, exit;
|
||||
#ifdef ENABLE_LOGGING_AND_PROFILING
|
||||
|
@ -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 {
|
||||
@ -69,10 +65,10 @@ int JavaScriptFrame::GetProvidedParametersCount() const {
|
||||
}
|
||||
|
||||
|
||||
void ExitFrame::Iterate(ObjectVisitor* a) const {
|
||||
// Exit frames on X64 do not contain any pointers. The arguments
|
||||
// are traversed as part of the expression stack of the calling
|
||||
// frame.
|
||||
void ExitFrame::Iterate(ObjectVisitor* v) const {
|
||||
v->VisitPointer(&code_slot());
|
||||
// The arguments are traversed as part of the expression stack of
|
||||
// the calling frame.
|
||||
}
|
||||
|
||||
byte* InternalFrame::GetCallerStackPointer() const {
|
||||
|
@ -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_
|
||||
|
@ -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();
|
||||
@ -3236,53 +3000,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>();
|
||||
@ -6146,13 +5863,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;
|
||||
@ -6261,9 +5982,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_);
|
||||
}
|
||||
|
||||
|
||||
@ -6751,53 +6474,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