Browse Source

Upgrade V8 to 2.2.17

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
bc76624ec7
  1. 11
      deps/v8/ChangeLog
  2. 5
      deps/v8/SConstruct
  3. 131
      deps/v8/src/arm/codegen-arm.cc
  4. 15
      deps/v8/src/arm/codegen-arm.h
  5. 6
      deps/v8/src/arm/full-codegen-arm.cc
  6. 15
      deps/v8/src/arm/macro-assembler-arm.cc
  7. 14
      deps/v8/src/arm/macro-assembler-arm.h
  8. 34
      deps/v8/src/debug.cc
  9. 3
      deps/v8/src/debug.h
  10. 2
      deps/v8/src/flag-definitions.h
  11. 2
      deps/v8/src/full-codegen.cc
  12. 2
      deps/v8/src/full-codegen.h
  13. 9
      deps/v8/src/heap-inl.h
  14. 6
      deps/v8/src/heap.cc
  15. 101
      deps/v8/src/ia32/codegen-ia32.cc
  16. 10
      deps/v8/src/ia32/codegen-ia32.h
  17. 6
      deps/v8/src/ia32/full-codegen-ia32.cc
  18. 361
      deps/v8/src/ia32/ic-ia32.cc
  19. 24
      deps/v8/src/ic.cc
  20. 2
      deps/v8/src/ic.h
  21. 2
      deps/v8/src/mips/full-codegen-mips.cc
  22. 50
      deps/v8/src/mirror-debugger.js
  23. 11
      deps/v8/src/profile-generator-inl.h
  24. 793
      deps/v8/src/profile-generator.cc
  25. 309
      deps/v8/src/profile-generator.h
  26. 55
      deps/v8/src/runtime.cc
  27. 27
      deps/v8/src/spaces-inl.h
  28. 1
      deps/v8/src/spaces.h
  29. 8
      deps/v8/src/v8-counters.h
  30. 2
      deps/v8/src/version.cc
  31. 79
      deps/v8/src/x64/codegen-x64.cc
  32. 16
      deps/v8/src/x64/codegen-x64.h
  33. 6
      deps/v8/src/x64/full-codegen-x64.cc
  34. 2
      deps/v8/test/cctest/test-heap.cc
  35. 163
      deps/v8/test/mjsunit/debug-return-value.js
  36. 96
      deps/v8/test/mjsunit/keyed-call-generic.js

11
deps/v8/ChangeLog

@ -1,6 +1,13 @@
2010-06-07: Version 2.2.16 2010-06-14: Version 2.2.17
Remove the SetExternalStringDiposeCallback API. Changed the Improved debugger support for stepping out of functions.
Incremental performance improvements.
2010-06-09: Version 2.2.16
Removed the SetExternalStringDiposeCallback API. Changed the
disposal of external string resources to call a virtual Dispose disposal of external string resources to call a virtual Dispose
method on the resource. method on the resource.

5
deps/v8/SConstruct

@ -781,6 +781,7 @@ def GetOptions():
result = Options() result = Options()
result.Add('mode', 'compilation mode (debug, release)', 'release') result.Add('mode', 'compilation mode (debug, release)', 'release')
result.Add('sample', 'build sample (shell, process, lineprocessor)', '') result.Add('sample', 'build sample (shell, process, lineprocessor)', '')
result.Add('cache', 'directory to use for scons build cache', '')
result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '') result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '')
result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '') result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '')
for (name, option) in SIMPLE_OPTIONS.iteritems(): for (name, option) in SIMPLE_OPTIONS.iteritems():
@ -862,6 +863,8 @@ def VerifyOptions(env):
Abort("Shared Object soname not applicable for static library.") Abort("Shared Object soname not applicable for static library.")
if env['os'] != 'win32' and env['pgo'] != 'off': if env['os'] != 'win32' and env['pgo'] != 'off':
Abort("Profile guided optimization only supported on Windows.") Abort("Profile guided optimization only supported on Windows.")
if env['cache'] and not os.path.isdir(env['cache']):
Abort("The specified cache directory does not exist.")
if not (env['arch'] == 'arm' or env['simulator'] == 'arm') and ('unalignedaccesses' in ARGUMENTS): if not (env['arch'] == 'arm' or env['simulator'] == 'arm') and ('unalignedaccesses' in ARGUMENTS):
print env['arch'] print env['arch']
print env['simulator'] print env['simulator']
@ -1130,6 +1133,8 @@ def Build():
else: else:
env.Default('library') env.Default('library')
if env['cache']:
CacheDir(env['cache'])
# We disable deprecation warnings because we need to be able to use # We disable deprecation warnings because we need to be able to use
# env.Copy without getting warnings for compatibility with older # env.Copy without getting warnings for compatibility with older

131
deps/v8/src/arm/codegen-arm.cc

@ -5087,18 +5087,28 @@ void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) { void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1); ASSERT_EQ(args->length(), 1);
// Load the argument on the stack and jump to the runtime.
Load(args->at(0)); Load(args->at(0));
frame_->CallRuntime(Runtime::kMath_sin, 1); if (CpuFeatures::IsSupported(VFP3)) {
TranscendentalCacheStub stub(TranscendentalCache::SIN);
frame_->SpillAllButCopyTOSToR0();
frame_->CallStub(&stub, 1);
} else {
frame_->CallRuntime(Runtime::kMath_sin, 1);
}
frame_->EmitPush(r0); frame_->EmitPush(r0);
} }
void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) { void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1); ASSERT_EQ(args->length(), 1);
// Load the argument on the stack and jump to the runtime.
Load(args->at(0)); Load(args->at(0));
frame_->CallRuntime(Runtime::kMath_cos, 1); if (CpuFeatures::IsSupported(VFP3)) {
TranscendentalCacheStub stub(TranscendentalCache::COS);
frame_->SpillAllButCopyTOSToR0();
frame_->CallStub(&stub, 1);
} else {
frame_->CallRuntime(Runtime::kMath_cos, 1);
}
frame_->EmitPush(r0); frame_->EmitPush(r0);
} }
@ -7090,7 +7100,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
CpuFeatures::Scope scope(VFP3); CpuFeatures::Scope scope(VFP3);
__ CheckMap(object, __ CheckMap(object,
scratch1, scratch1,
Factory::heap_number_map(), Heap::kHeapNumberMapRootIndex,
not_found, not_found,
true); true);
@ -8236,6 +8246,110 @@ Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
} }
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
// Argument is a number and is on stack and in r0.
Label runtime_call;
Label input_not_smi;
Label loaded;
if (CpuFeatures::IsSupported(VFP3)) {
// Load argument and check if it is a smi.
__ BranchOnNotSmi(r0, &input_not_smi);
CpuFeatures::Scope scope(VFP3);
// Input is a smi. Convert to double and load the low and high words
// of the double into r2, r3.
__ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
__ b(&loaded);
__ bind(&input_not_smi);
// Check if input is a HeapNumber.
__ CheckMap(r0,
r1,
Heap::kHeapNumberMapRootIndex,
&runtime_call,
true);
// Input is a HeapNumber. Load it to a double register and store the
// low and high words into r2, r3.
__ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
__ bind(&loaded);
// r2 = low 32 bits of double value
// r3 = high 32 bits of double value
// Compute hash:
// h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
__ eor(r1, r2, Operand(r3));
__ eor(r1, r1, Operand(r1, LSR, 16));
__ eor(r1, r1, Operand(r1, LSR, 8));
ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
if (CpuFeatures::IsSupported(ARMv7)) {
const int kTranscendentalCacheSizeBits = 9;
ASSERT_EQ(1 << kTranscendentalCacheSizeBits,
TranscendentalCache::kCacheSize);
__ ubfx(r1, r1, 0, kTranscendentalCacheSizeBits);
} else {
__ and_(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
}
// r2 = low 32 bits of double value.
// r3 = high 32 bits of double value.
// r1 = TranscendentalCache::hash(double value).
__ mov(r0,
Operand(ExternalReference::transcendental_cache_array_address()));
// r0 points to cache array.
__ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
// r0 points to the cache for the type type_.
// If NULL, the cache hasn't been initialized yet, so go through runtime.
__ cmp(r0, Operand(0));
__ b(eq, &runtime_call);
#ifdef DEBUG
// Check that the layout of cache elements match expectations.
{ TranscendentalCache::Element test_elem[2];
char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
CHECK_EQ(0, elem_in0 - elem_start);
CHECK_EQ(kIntSize, elem_in1 - elem_start);
CHECK_EQ(2 * kIntSize, elem_out - elem_start);
}
#endif
// Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
__ add(r1, r1, Operand(r1, LSL, 1));
__ add(r0, r0, Operand(r1, LSL, 2));
// Check if cache matches: Double value is stored in uint32_t[2] array.
__ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
__ cmp(r2, r4);
__ b(ne, &runtime_call);
__ cmp(r3, r5);
__ b(ne, &runtime_call);
// Cache hit. Load result, pop argument and return.
__ mov(r0, Operand(r6));
__ pop();
__ Ret();
}
__ bind(&runtime_call);
__ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
}
Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
switch (type_) {
// Add more cases when necessary.
case TranscendentalCache::SIN: return Runtime::kMath_sin;
case TranscendentalCache::COS: return Runtime::kMath_cos;
default:
UNIMPLEMENTED();
return Runtime::kAbort;
}
}
void StackCheckStub::Generate(MacroAssembler* masm) { void StackCheckStub::Generate(MacroAssembler* masm) {
// Do tail-call to runtime routine. Runtime routines expect at least one // Do tail-call to runtime routine. Runtime routines expect at least one
// argument, so give it a Smi. // argument, so give it a Smi.
@ -9550,8 +9664,11 @@ void StringCharCodeAtGenerator::GenerateSlow(
// Index is not a smi. // Index is not a smi.
__ bind(&index_not_smi_); __ bind(&index_not_smi_);
// If index is a heap number, try converting it to an integer. // If index is a heap number, try converting it to an integer.
__ CheckMap(index_, scratch_, __ CheckMap(index_,
Factory::heap_number_map(), index_not_number_, true); scratch_,
Heap::kHeapNumberMapRootIndex,
index_not_number_,
true);
call_helper.BeforeCall(masm); call_helper.BeforeCall(masm);
__ Push(object_, index_); __ Push(object_, index_);
__ push(index_); // Consumed by runtime conversion function. __ push(index_); // Consumed by runtime conversion function.

15
deps/v8/src/arm/codegen-arm.h

@ -589,6 +589,21 @@ class CodeGenerator: public AstVisitor {
}; };
// Compute a transcendental math function natively, or call the
// TranscendentalCache runtime function.
class TranscendentalCacheStub: public CodeStub {
public:
explicit TranscendentalCacheStub(TranscendentalCache::Type type)
: type_(type) {}
void Generate(MacroAssembler* masm);
private:
TranscendentalCache::Type type_;
Major MajorKey() { return TranscendentalCache; }
int MinorKey() { return type_; }
Runtime::FunctionId RuntimeFunction();
};
class GenericBinaryOpStub : public CodeStub { class GenericBinaryOpStub : public CodeStub {
public: public:
GenericBinaryOpStub(Token::Value op, GenericBinaryOpStub(Token::Value op,

6
deps/v8/src/arm/full-codegen-arm.cc

@ -196,11 +196,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) {
// body. // body.
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex); __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
} }
EmitReturnSequence(function()->end_position()); EmitReturnSequence();
} }
void FullCodeGenerator::EmitReturnSequence(int position) { void FullCodeGenerator::EmitReturnSequence() {
Comment cmnt(masm_, "[ Return sequence"); Comment cmnt(masm_, "[ Return sequence");
if (return_label_.is_bound()) { if (return_label_.is_bound()) {
__ b(&return_label_); __ b(&return_label_);
@ -224,7 +224,7 @@ void FullCodeGenerator::EmitReturnSequence(int position) {
// Here we use masm_-> instead of the __ macro to avoid the code coverage // Here we use masm_-> instead of the __ macro to avoid the code coverage
// tool from instrumenting as we rely on the code size here. // tool from instrumenting as we rely on the code size here.
int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize; int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
CodeGenerator::RecordPositions(masm_, position); CodeGenerator::RecordPositions(masm_, function()->end_position());
__ RecordJSReturn(); __ RecordJSReturn();
masm_->mov(sp, fp); masm_->mov(sp, fp);
masm_->ldm(ia_w, sp, fp.bit() | lr.bit()); masm_->ldm(ia_w, sp, fp.bit() | lr.bit());

15
deps/v8/src/arm/macro-assembler-arm.cc

@ -1199,6 +1199,21 @@ void MacroAssembler::CheckMap(Register obj,
} }
void MacroAssembler::CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
bool is_heap_object) {
if (!is_heap_object) {
BranchOnSmi(obj, fail);
}
ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
LoadRoot(ip, index);
cmp(scratch, ip);
b(ne, fail);
}
void MacroAssembler::TryGetFunctionPrototype(Register function, void MacroAssembler::TryGetFunctionPrototype(Register function,
Register result, Register result,
Register scratch, Register scratch,

14
deps/v8/src/arm/macro-assembler-arm.h

@ -401,15 +401,23 @@ class MacroAssembler: public Assembler {
InstanceType type); InstanceType type);
// Check if the map of an object is equal to a specified map and // Check if the map of an object is equal to a specified map (either
// branch to label if not. Skip the smi check if not required // given directly or as an index into the root list) and branch to
// (object is known to be a heap object) // label if not. Skip the smi check if not required (object is known
// to be a heap object)
void CheckMap(Register obj, void CheckMap(Register obj,
Register scratch, Register scratch,
Handle<Map> map, Handle<Map> map,
Label* fail, Label* fail,
bool is_heap_object); bool is_heap_object);
void CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
bool is_heap_object);
// Load and check the instance type of an object for being a string. // Load and check the instance type of an object for being a string.
// Loads the type into the second argument register. // Loads the type into the second argument register.
// Returns a condition that will be enabled if the object was a string. // Returns a condition that will be enabled if the object was a string.

34
deps/v8/src/debug.cc

@ -1713,6 +1713,40 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
} }
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
HandleScope scope;
// Get the executing function in which the debug break occurred.
Handle<SharedFunctionInfo> shared =
Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
if (!EnsureDebugInfo(shared)) {
// Return if we failed to retrieve the debug info.
return false;
}
Handle<DebugInfo> debug_info = GetDebugInfo(shared);
Handle<Code> code(debug_info->code());
#ifdef DEBUG
// Get the code which is actually executing.
Handle<Code> frame_code(frame->code());
ASSERT(frame_code.is_identical_to(code));
#endif
// Find the call address in the running code.
Address addr = frame->pc() - Assembler::kCallTargetAddressOffset;
// Check if the location is at JS return.
RelocIterator it(debug_info->code());
while (!it.done()) {
if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
return (it.rinfo()->pc() ==
addr - Assembler::kPatchReturnSequenceAddressOffset);
}
it.next();
}
return false;
}
void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id) { void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id) {
thread_local_.frames_are_dropped_ = true; thread_local_.frames_are_dropped_ = true;
thread_local_.break_frame_id_ = new_break_frame_id; thread_local_.break_frame_id_ = new_break_frame_id;

3
deps/v8/src/debug.h

@ -270,6 +270,9 @@ class Debug {
// Check whether a global object is the debug global object. // Check whether a global object is the debug global object.
static bool IsDebugGlobal(GlobalObject* global); static bool IsDebugGlobal(GlobalObject* global);
// Check whether this frame is just about to return.
static bool IsBreakAtReturn(JavaScriptFrame* frame);
// Fast check to see if any break points are active. // Fast check to see if any break points are active.
inline static bool has_break_points() { return has_break_points_; } inline static bool has_break_points() { return has_break_points_; }

2
deps/v8/src/flag-definitions.h

@ -191,6 +191,8 @@ DEFINE_bool(trace_gc_verbose, false,
"print more details following each garbage collection") "print more details following each garbage collection")
DEFINE_bool(collect_maps, true, DEFINE_bool(collect_maps, true,
"garbage collect maps from which no objects can be reached") "garbage collect maps from which no objects can be reached")
DEFINE_bool(flush_code, false,
"flush code that we expect not to use again before full gc")
// v8.cc // v8.cc
DEFINE_bool(use_idle_notification, true, DEFINE_bool(use_idle_notification, true,

2
deps/v8/src/full-codegen.cc

@ -1077,7 +1077,7 @@ void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
} }
__ Drop(stack_depth); __ Drop(stack_depth);
EmitReturnSequence(stmt->statement_pos()); EmitReturnSequence();
} }

2
deps/v8/src/full-codegen.h

@ -389,7 +389,7 @@ class FullCodeGenerator: public AstVisitor {
FunctionLiteral* function); FunctionLiteral* function);
// Platform-specific return sequence // Platform-specific return sequence
void EmitReturnSequence(int position); void EmitReturnSequence();
// Platform-specific code sequences for calls // Platform-specific code sequences for calls
void EmitCallWithStub(Call* expr); void EmitCallWithStub(Call* expr);

9
deps/v8/src/heap-inl.h

@ -196,12 +196,9 @@ void Heap::RecordWrite(Address address, int offset) {
void Heap::RecordWrites(Address address, int start, int len) { void Heap::RecordWrites(Address address, int start, int len) {
if (new_space_.Contains(address)) return; if (new_space_.Contains(address)) return;
ASSERT(!new_space_.FromSpaceContains(address)); ASSERT(!new_space_.FromSpaceContains(address));
for (int offset = start; Page* page = Page::FromAddress(address);
offset < start + len * kPointerSize; page->SetRegionMarks(page->GetRegionMarks() |
offset += kPointerSize) { page->GetRegionMaskForSpan(address + start, len * kPointerSize));
SLOW_ASSERT(Contains(address + offset));
Page::FromAddress(address)->MarkRegionDirty(address + offset);
}
} }

6
deps/v8/src/heap.cc

@ -605,8 +605,10 @@ void Heap::PerformGarbageCollection(AllocationSpace space,
EnsureFromSpaceIsCommitted(); EnsureFromSpaceIsCommitted();
if (collector == MARK_COMPACTOR) { if (collector == MARK_COMPACTOR) {
// Flush all potentially unused code. if (FLAG_flush_code) {
FlushCode(); // Flush all potentially unused code.
FlushCode();
}
// Perform mark-sweep with optional compaction. // Perform mark-sweep with optional compaction.
MarkCompact(tracer); MarkCompact(tracer);

101
deps/v8/src/ia32/codegen-ia32.cc

@ -1446,10 +1446,40 @@ bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
} }
static void CheckTwoForSminess(MacroAssembler* masm, void CodeGenerator::JumpIfNotBothSmiUsingTypeInfo(Register left,
Register left, Register right, Register scratch, Register right,
TypeInfo left_info, TypeInfo right_info, Register scratch,
DeferredInlineBinaryOperation* deferred); TypeInfo left_info,
TypeInfo right_info,
DeferredCode* deferred) {
if (left.is(right)) {
if (!left_info.IsSmi()) {
__ test(left, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(left);
}
} else if (!left_info.IsSmi()) {
if (!right_info.IsSmi()) {
__ mov(scratch, left);
__ or_(scratch, Operand(right));
__ test(scratch, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
__ test(left, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
if (FLAG_debug_code) __ AbortIfNotSmi(right);
}
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(left);
if (!right_info.IsSmi()) {
__ test(right, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(right);
}
}
}
// Implements a binary operation using a deferred code object and some // Implements a binary operation using a deferred code object and some
@ -1539,19 +1569,11 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
left_type_info, left_type_info,
right_type_info, right_type_info,
overwrite_mode); overwrite_mode);
if (left->reg().is(right->reg())) { JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), edx,
__ test(left->reg(), Immediate(kSmiTagMask)); left_type_info, right_type_info, deferred);
} else { if (!left_is_in_eax) {
// Use the quotient register as a scratch for the tag check. __ mov(eax, left->reg());
if (!left_is_in_eax) __ mov(eax, left->reg());
left_is_in_eax = false; // About to destroy the value in eax.
__ or_(eax, Operand(right->reg()));
ASSERT(kSmiTag == 0); // Adjust test if not the case.
__ test(eax, Immediate(kSmiTagMask));
} }
deferred->Branch(not_zero);
if (!left_is_in_eax) __ mov(eax, left->reg());
// Sign extend eax into edx:eax. // Sign extend eax into edx:eax.
__ cdq(); __ cdq();
// Check for 0 divisor. // Check for 0 divisor.
@ -1674,8 +1696,8 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
__ cmp(answer.reg(), 0xc0000000); __ cmp(answer.reg(), 0xc0000000);
deferred->Branch(negative); deferred->Branch(negative);
} else { } else {
CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(), JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), answer.reg(),
left_type_info, right_type_info, deferred); left_type_info, right_type_info, deferred);
// Untag both operands. // Untag both operands.
__ mov(answer.reg(), left->reg()); __ mov(answer.reg(), left->reg());
@ -1751,8 +1773,8 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
left_type_info, left_type_info,
right_type_info, right_type_info,
overwrite_mode); overwrite_mode);
CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(), JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), answer.reg(),
left_type_info, right_type_info, deferred); left_type_info, right_type_info, deferred);
__ mov(answer.reg(), left->reg()); __ mov(answer.reg(), left->reg());
switch (op) { switch (op) {
@ -8976,40 +8998,6 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
#define __ ACCESS_MASM(masm) #define __ ACCESS_MASM(masm)
static void CheckTwoForSminess(MacroAssembler* masm,
Register left, Register right, Register scratch,
TypeInfo left_info, TypeInfo right_info,
DeferredInlineBinaryOperation* deferred) {
if (left.is(right)) {
if (!left_info.IsSmi()) {
__ test(left, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(left);
}
} else if (!left_info.IsSmi()) {
if (!right_info.IsSmi()) {
__ mov(scratch, left);
__ or_(scratch, Operand(right));
__ test(scratch, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
__ test(left, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
if (FLAG_debug_code) __ AbortIfNotSmi(right);
}
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(left);
if (!right_info.IsSmi()) {
__ test(right, Immediate(kSmiTagMask));
deferred->Branch(not_zero);
} else {
if (FLAG_debug_code) __ AbortIfNotSmi(right);
}
}
}
Handle<String> Reference::GetName() { Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED); ASSERT(type_ == NAMED);
Property* property = expression_->AsProperty(); Property* property = expression_->AsProperty();
@ -10302,13 +10290,14 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ mov(ecx, ebx); __ mov(ecx, ebx);
__ xor_(ecx, Operand(edx)); __ xor_(ecx, Operand(edx));
__ mov(eax, ecx); __ mov(eax, ecx);
__ sar(eax, 16); __ shr(eax, 16);
__ xor_(ecx, Operand(eax)); __ xor_(ecx, Operand(eax));
__ mov(eax, ecx); __ mov(eax, ecx);
__ sar(eax, 8); __ shr(eax, 8);
__ xor_(ecx, Operand(eax)); __ xor_(ecx, Operand(eax));
ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize)); ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
__ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1)); __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1));
// ST[0] == double value. // ST[0] == double value.
// ebx = low 32 bits of double value. // ebx = low 32 bits of double value.
// edx = high 32 bits of double value. // edx = high 32 bits of double value.

10
deps/v8/src/ia32/codegen-ia32.h

@ -519,6 +519,16 @@ class CodeGenerator: public AstVisitor {
void GenericBinaryOperation(BinaryOperation* expr, void GenericBinaryOperation(BinaryOperation* expr,
OverwriteMode overwrite_mode); OverwriteMode overwrite_mode);
// Emits code sequence that jumps to deferred code if the inputs
// are not both smis. Cannot be in MacroAssembler because it takes
// advantage of TypeInfo to skip unneeded checks.
void JumpIfNotBothSmiUsingTypeInfo(Register left,
Register right,
Register scratch,
TypeInfo left_info,
TypeInfo right_info,
DeferredCode* deferred);
// If possible, combine two constant smi values using op to produce // If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time. // a smi result, and push it on the virtual frame, all at compile time.
// Returns true if it succeeds. Otherwise it has no effect. // Returns true if it succeeds. Otherwise it has no effect.

6
deps/v8/src/ia32/full-codegen-ia32.cc

@ -186,12 +186,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) {
{ Comment cmnt(masm_, "[ return <undefined>;"); { Comment cmnt(masm_, "[ return <undefined>;");
// Emit a 'return undefined' in case control fell off the end of the body. // Emit a 'return undefined' in case control fell off the end of the body.
__ mov(eax, Factory::undefined_value()); __ mov(eax, Factory::undefined_value());
EmitReturnSequence(function()->end_position()); EmitReturnSequence();
} }
} }
void FullCodeGenerator::EmitReturnSequence(int position) { void FullCodeGenerator::EmitReturnSequence() {
Comment cmnt(masm_, "[ Return sequence"); Comment cmnt(masm_, "[ Return sequence");
if (return_label_.is_bound()) { if (return_label_.is_bound()) {
__ jmp(&return_label_); __ jmp(&return_label_);
@ -207,7 +207,7 @@ void FullCodeGenerator::EmitReturnSequence(int position) {
Label check_exit_codesize; Label check_exit_codesize;
masm_->bind(&check_exit_codesize); masm_->bind(&check_exit_codesize);
#endif #endif
CodeGenerator::RecordPositions(masm_, position); CodeGenerator::RecordPositions(masm_, function()->end_position());
__ RecordJSReturn(); __ RecordJSReturn();
// Do not use the leave instruction here because it is too short to // Do not use the leave instruction here because it is too short to
// patch with the code required by the debugger. // patch with the code required by the debugger.

361
deps/v8/src/ia32/ic-ia32.cc

@ -57,6 +57,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
Register r0, Register r0,
Register r1, Register r1,
Register r2, Register r2,
Register result,
DictionaryCheck check_dictionary) { DictionaryCheck check_dictionary) {
// Register use: // Register use:
// //
@ -66,9 +67,10 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
// r0 - used to hold the property dictionary. // r0 - used to hold the property dictionary.
// //
// r1 - used for the index into the property dictionary // r1 - used for the index into the property dictionary
// - holds the result on exit.
// //
// r2 - used to hold the capacity of the property dictionary. // r2 - used to hold the capacity of the property dictionary.
//
// result - holds the result on exit.
Label done; Label done;
@ -149,7 +151,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
// Get the value at the masked, scaled index. // Get the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize; const int kValueOffset = kElementsStartOffset + kPointerSize;
__ mov(r1, Operand(r0, r1, times_4, kValueOffset - kHeapObjectTag)); __ mov(result, Operand(r0, r1, times_4, kValueOffset - kHeapObjectTag));
} }
@ -159,14 +161,13 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Register key, Register key,
Register r0, Register r0,
Register r1, Register r1,
Register r2) { Register r2,
Register result) {
// Register use: // Register use:
// //
// elements - holds the slow-case elements of the receiver and is unchanged. // elements - holds the slow-case elements of the receiver and is unchanged.
// //
// key - holds the smi key on entry and is unchanged if a branch is // key - holds the smi key on entry and is unchanged.
// performed to the miss label. If the load succeeds and we
// fall through, key holds the result on exit.
// //
// Scratch registers: // Scratch registers:
// //
@ -175,6 +176,9 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
// r1 - used to hold the capacity mask of the dictionary // r1 - used to hold the capacity mask of the dictionary
// //
// r2 - used for the index into the dictionary. // r2 - used for the index into the dictionary.
//
// result - holds the result on exit if the load succeeds and we fall through.
Label done; Label done;
// Compute the hash code from the untagged key. This must be kept in sync // Compute the hash code from the untagged key. This must be kept in sync
@ -246,7 +250,7 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
// Get the value at the masked, scaled index. // Get the value at the masked, scaled index.
const int kValueOffset = const int kValueOffset =
NumberDictionary::kElementsStartOffset + kPointerSize; NumberDictionary::kElementsStartOffset + kPointerSize;
__ mov(key, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); __ mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
} }
@ -298,52 +302,158 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
} }
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Checks the receiver for special cases (value type, slow case bits).
// ----------- S t a t e ------------- // Falls through for regular JS object.
// -- eax : key static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
// -- edx : receiver Register receiver,
// -- esp[0] : return address Register r0,
// ----------------------------------- Label* slow) {
Label slow, check_string, index_smi, index_string; // Register use:
Label check_pixel_array, probe_dictionary, check_number_dictionary; // receiver - holds the receiver and is unchanged.
// Scratch registers:
// r0 - used to hold the map of the receiver.
// Check that the object isn't a smi. // Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask)); __ test(receiver, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken); __ j(zero, slow, not_taken);
// Get the map of the receiver. // Get the map of the receiver.
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
// Check bit field. // Check bit field.
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset), kSlowCaseBitFieldMask); __ test_b(FieldOperand(r0, Map::kBitFieldOffset),
__ j(not_zero, &slow, not_taken); KeyedLoadIC::kSlowCaseBitFieldMask);
__ j(not_zero, slow, not_taken);
// Check that the object is some kind of JS object EXCEPT JS Value type. // Check that the object is some kind of JS object EXCEPT JS Value type.
// In the case that the object is a value-wrapper object, // In the case that the object is a value-wrapper object,
// we enter the runtime system to make sure that indexing // we enter the runtime system to make sure that indexing
// into string objects work as intended. // into string objects works as intended.
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
__ CmpInstanceType(ecx, JS_OBJECT_TYPE);
__ j(below, &slow, not_taken); __ CmpInstanceType(r0, JS_OBJECT_TYPE);
// Check that the key is a smi. __ j(below, slow, not_taken);
__ test(eax, Immediate(kSmiTagMask)); }
__ j(not_zero, &check_string, not_taken);
__ bind(&index_smi);
// Now the key is known to be a smi. This place is also jumped to from below // Loads an indexed element from a fast case array.
// where a numeric string is converted to a smi. static void GenerateFastArrayLoad(MacroAssembler* masm,
__ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); Register receiver,
Register key,
Register scratch,
Register result,
Label* not_fast_array,
Label* out_of_range) {
// Register use:
// receiver - holds the receiver and is unchanged.
// key - holds the key and is unchanged (must be a smi).
// Scratch registers:
// scratch - used to hold elements of the receiver and the loaded value.
// result - holds the result on exit if the load succeeds and
// we fall through.
__ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary). // Check that the object is in fast mode (not dictionary).
__ CheckMap(ecx, Factory::fixed_array_map(), &check_pixel_array, true); __ CheckMap(scratch, Factory::fixed_array_map(), not_fast_array, true);
// Check that the key (index) is within bounds. // Check that the key (index) is within bounds.
__ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset)); __ cmp(key, FieldOperand(scratch, FixedArray::kLengthOffset));
__ j(above_equal, &slow); __ j(above_equal, out_of_range);
// Fast case: Do the load. // Fast case: Do the load.
ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0)); ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0));
__ mov(ecx, FieldOperand(ecx, eax, times_2, FixedArray::kHeaderSize)); __ mov(scratch, FieldOperand(scratch, key, times_2, FixedArray::kHeaderSize));
__ cmp(Operand(ecx), Immediate(Factory::the_hole_value())); __ cmp(Operand(scratch), Immediate(Factory::the_hole_value()));
// In case the loaded value is the_hole we have to consult GetProperty // In case the loaded value is the_hole we have to consult GetProperty
// to ensure the prototype chain is searched. // to ensure the prototype chain is searched.
__ j(equal, &slow); __ j(equal, out_of_range);
__ mov(eax, ecx); if (!result.is(scratch)) {
__ mov(result, scratch);
}
}
// Checks whether a key is an array index string or a symbol string.
// Falls through if a key is a symbol.
static void GenerateKeyStringCheck(MacroAssembler* masm,
Register key,
Register map,
Register hash,
Label* index_string,
Label* not_symbol) {
// Register use:
// key - holds the key and is unchanged. Assumed to be non-smi.
// Scratch registers:
// map - used to hold the map of the key.
// hash - used to hold the hash of the key.
__ CmpObjectType(key, FIRST_NONSTRING_TYPE, map);
__ j(above_equal, not_symbol);
// Is the string an array index, with cached numeric value?
__ mov(hash, FieldOperand(key, String::kHashFieldOffset));
__ test(hash, Immediate(String::kContainsCachedArrayIndexMask));
__ j(zero, index_string, not_taken);
// Is the string a symbol?
ASSERT(kSymbolTag != 0);
__ test_b(FieldOperand(map, Map::kInstanceTypeOffset), kIsSymbolMask);
__ j(zero, not_symbol, not_taken);
}
// Picks out an array index from the hash field.
// The generated code never falls through.
static void GenerateIndexFromHash(MacroAssembler* masm,
Register key,
Register hash,
Label* index_smi) {
// Register use:
// key - holds the overwritten key on exit.
// hash - holds the key's hash. Clobbered.
// The assert checks that the constants for the maximum number of digits
// for an array index cached in the hash field and the number of bits
// reserved for it does not conflict.
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
(1 << String::kArrayIndexValueBits));
// We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
// the low kHashShift bits.
// key: string key
// ebx: hash field.
ASSERT(String::kHashShift >= kSmiTagSize);
__ and_(hash, String::kArrayIndexValueMask);
__ shr(hash, String::kHashShift - kSmiTagSize);
// Here we actually clobber the key which will be used if calling into
// runtime later. However as the new key is the numeric value of a string key
// there is no difference in using either key.
__ mov(key, hash);
// Now jump to the place where smi keys are handled.
__ jmp(index_smi);
}
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label slow, check_string, index_smi, index_string;
Label check_pixel_array, probe_dictionary, check_number_dictionary;
GenerateKeyedLoadReceiverCheck(masm, edx, ecx, &slow);
// Check that the key is a smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &check_string, not_taken);
__ bind(&index_smi);
// Now the key is known to be a smi. This place is also jumped to from
// where a numeric string is converted to a smi.
GenerateFastArrayLoad(masm,
edx,
eax,
ecx,
eax,
&check_pixel_array,
&slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1); __ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
__ ret(0); __ ret(0);
@ -379,7 +489,8 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
eax, eax,
ebx, ebx,
edx, edx,
edi); edi,
eax);
// Pop receiver before returning. // Pop receiver before returning.
__ pop(edx); __ pop(edx);
__ ret(0); __ ret(0);
@ -396,22 +507,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
GenerateRuntimeGetProperty(masm); GenerateRuntimeGetProperty(masm);
__ bind(&check_string); __ bind(&check_string);
// The key is not a smi. GenerateKeyStringCheck(masm, eax, ecx, ebx, &index_string, &slow);
// Is it a string?
// edx: receiver
// eax: key
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &slow);
// Is the string an array index, with cached numeric value?
__ mov(ebx, FieldOperand(eax, String::kHashFieldOffset));
__ test(ebx, Immediate(String::kContainsCachedArrayIndexMask));
__ j(zero, &index_string, not_taken);
// Is the string a symbol?
// ecx: key map.
ASSERT(kSymbolTag != 0);
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kIsSymbolMask);
__ j(zero, &slow, not_taken);
// If the receiver is a fast-case object, check the keyed lookup // If the receiver is a fast-case object, check the keyed lookup
// cache. Otherwise probe the dictionary. // cache. Otherwise probe the dictionary.
@ -472,32 +568,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
ebx, ebx,
ecx, ecx,
edi, edi,
eax,
DICTIONARY_CHECK_DONE); DICTIONARY_CHECK_DONE);
__ mov(eax, ecx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0); __ ret(0);
// If the hash field contains an array index pick it out. The assert checks
// that the constants for the maximum number of digits for an array index
// cached in the hash field and the number of bits reserved for it does not
// conflict.
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
(1 << String::kArrayIndexValueBits));
__ bind(&index_string); __ bind(&index_string);
// We want the smi-tagged index in eax. kArrayIndexValueMask has zeros in GenerateIndexFromHash(masm, eax, ebx, &index_smi);
// the low kHashShift bits.
// eax: key (string).
// ebx: hash field.
// edx: receiver.
ASSERT(String::kHashShift >= kSmiTagSize);
__ and_(ebx, String::kArrayIndexValueMask);
__ shr(ebx, String::kHashShift - kSmiTagSize);
// Here we actually clobber the key (eax) which will be used if calling into
// runtime later. However as the new key is the numeric value of a string key
// there is no difference in using either key.
__ mov(eax, ebx);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
} }
@ -1115,7 +1192,8 @@ static void GenerateNormalHelper(MacroAssembler* masm,
// Search dictionary - put result in register edi. // Search dictionary - put result in register edi.
__ mov(edi, edx); __ mov(edi, edx);
GenerateDictionaryLoad(masm, miss, edx, ecx, eax, edi, ebx, CHECK_DICTIONARY); GenerateDictionaryLoad(
masm, miss, edx, ecx, eax, edi, ebx, edi, CHECK_DICTIONARY);
// Check that the result is not a smi. // Check that the result is not a smi.
__ test(edi, Immediate(kSmiTagMask)); __ test(edi, Immediate(kSmiTagMask));
@ -1293,47 +1371,123 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address. // Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
Label miss, skip_probe; Label do_call, slow_call, slow_load, slow_reload_receiver;
Label check_number_dictionary, check_string, lookup_monomorphic_cache;
Label index_smi, index_string;
// Do not probe monomorphic cache if a key is a smi. // Check that the key is a smi.
__ test(ecx, Immediate(kSmiTagMask)); __ test(ecx, Immediate(kSmiTagMask));
__ j(equal, &skip_probe, taken); __ j(not_zero, &check_string, not_taken);
GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, &skip_probe);
__ bind(&skip_probe);
__ mov(eax, ecx); __ bind(&index_smi);
__ EnterInternalFrame(); // Now the key is known to be a smi. This place is also jumped to from
__ push(ecx); // where a numeric string is converted to a smi.
__ call(Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_Generic)),
RelocInfo::CODE_TARGET);
__ pop(ecx);
__ LeaveInternalFrame();
__ mov(edi, eax);
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); GenerateKeyedLoadReceiverCheck(masm, edx, eax, &slow_call);
// Check that the receiver isn't a smi. GenerateFastArrayLoad(masm,
__ test(edx, Immediate(kSmiTagMask)); edx,
__ j(zero, &miss, not_taken); ecx,
eax,
edi,
&check_number_dictionary,
&slow_load);
__ IncrementCounter(&Counters::keyed_call_generic_smi_fast, 1);
// Check that the receiver is a valid JS object. __ bind(&do_call);
__ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, eax); // receiver in edx is not used after this point.
__ j(below, &miss, not_taken); // ecx: key
// edi: function
// Check that the value is a JavaScript function. // Check that the value in edi is a JavaScript function.
__ test(edi, Immediate(kSmiTagMask)); __ test(edi, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken); __ j(zero, &slow_call, not_taken);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
__ j(not_equal, &miss, not_taken); __ j(not_equal, &slow_call, not_taken);
// Invoke the function. // Invoke the function.
ParameterCount actual(argc); ParameterCount actual(argc);
__ InvokeFunction(edi, actual, JUMP_FUNCTION); __ InvokeFunction(edi, actual, JUMP_FUNCTION);
__ bind(&miss); __ bind(&check_number_dictionary);
// eax: elements
// ecx: smi key
// Check whether the elements is a number dictionary.
__ CheckMap(eax, Factory::hash_table_map(), &slow_load, true);
__ mov(ebx, ecx);
__ SmiUntag(ebx);
// ebx: untagged index
// Receiver in edx will be clobbered, need to reload it on miss.
GenerateNumberDictionaryLoad(masm,
&slow_reload_receiver,
eax,
ecx,
ebx,
edx,
edi,
edi);
__ IncrementCounter(&Counters::keyed_call_generic_smi_dict, 1);
__ jmp(&do_call);
__ bind(&slow_reload_receiver);
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
__ bind(&slow_load);
// This branch is taken when calling KeyedCallIC_Miss is neither required
// nor beneficial.
__ IncrementCounter(&Counters::keyed_call_generic_slow_load, 1);
__ EnterInternalFrame();
__ push(ecx); // save the key
__ push(edx); // pass the receiver
__ push(ecx); // pass the key
__ CallRuntime(Runtime::kKeyedGetProperty, 2);
__ pop(ecx); // restore the key
__ LeaveInternalFrame();
__ mov(edi, eax);
__ jmp(&do_call);
__ bind(&check_string);
GenerateKeyStringCheck(masm, ecx, eax, ebx, &index_string, &slow_call);
// The key is known to be a symbol.
// If the receiver is a regular JS object with slow properties then do
// a quick inline probe of the receiver's dictionary.
// Otherwise do the monomorphic cache probe.
GenerateKeyedLoadReceiverCheck(masm, edx, eax, &lookup_monomorphic_cache);
__ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::hash_table_map()));
__ j(not_equal, &lookup_monomorphic_cache, not_taken);
GenerateDictionaryLoad(masm,
&slow_load,
edx,
ecx,
ebx,
eax,
edi,
edi,
DICTIONARY_CHECK_DONE);
__ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1);
__ jmp(&do_call);
__ bind(&lookup_monomorphic_cache);
__ IncrementCounter(&Counters::keyed_call_generic_lookup_cache, 1);
GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, &slow_call);
// Fall through on miss.
__ bind(&slow_call);
// This branch is taken if:
// - the receiver requires boxing or access check,
// - the key is neither smi nor symbol,
// - the value loaded is not a function,
// - there is hope that the runtime will create a monomorphic call stub
// that will get fetched next time.
__ IncrementCounter(&Counters::keyed_call_generic_slow, 1);
GenerateMiss(masm, argc); GenerateMiss(masm, argc);
__ bind(&index_string);
GenerateIndexFromHash(masm, ecx, ebx, &index_smi);
} }
@ -1410,6 +1564,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
edx, edx,
edi, edi,
ebx, ebx,
edi,
CHECK_DICTIONARY); CHECK_DICTIONARY);
__ mov(eax, edi); __ mov(eax, edi);
__ ret(0); __ ret(0);

24
deps/v8/src/ic.cc

@ -58,7 +58,7 @@ static char TransitionMarkFromState(IC::State state) {
} }
void IC::TraceIC(const char* type, void IC::TraceIC(const char* type,
Handle<String> name, Handle<Object> name,
State old_state, State old_state,
Code* new_target, Code* new_target,
const char* extra_info) { const char* extra_info) {
@ -610,15 +610,19 @@ Object* KeyedCallIC::LoadFunction(State state,
if (object->IsString() || object->IsNumber() || object->IsBoolean()) { if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
ReceiverToObject(object); ReceiverToObject(object);
} else { }
if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
int argc = target()->arguments_count(); if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
InLoopFlag in_loop = target()->ic_in_loop(); int argc = target()->arguments_count();
Object* code = StubCache::ComputeCallMegamorphic( InLoopFlag in_loop = target()->ic_in_loop();
argc, in_loop, Code::KEYED_CALL_IC); Object* code = StubCache::ComputeCallMegamorphic(
if (!code->IsFailure()) { argc, in_loop, Code::KEYED_CALL_IC);
set_target(Code::cast(code)); if (!code->IsFailure()) {
} set_target(Code::cast(code));
#ifdef DEBUG
TraceIC(
"KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
#endif
} }
} }
Object* result = Runtime::GetObjectProperty(object, key); Object* result = Runtime::GetObjectProperty(object, key);

2
deps/v8/src/ic.h

@ -140,7 +140,7 @@ class IC {
#ifdef DEBUG #ifdef DEBUG
static void TraceIC(const char* type, static void TraceIC(const char* type,
Handle<String> name, Handle<Object> name,
State old_state, State old_state,
Code* new_target, Code* new_target,
const char* extra_info = ""); const char* extra_info = "");

2
deps/v8/src/mips/full-codegen-mips.cc

@ -45,7 +45,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) {
} }
void FullCodeGenerator::EmitReturnSequence(int position) { void FullCodeGenerator::EmitReturnSequence() {
UNIMPLEMENTED_MIPS(); UNIMPLEMENTED_MIPS();
} }

50
deps/v8/src/mirror-debugger.js

@ -1240,8 +1240,9 @@ const kFrameDetailsArgumentCountIndex = 3;
const kFrameDetailsLocalCountIndex = 4; const kFrameDetailsLocalCountIndex = 4;
const kFrameDetailsSourcePositionIndex = 5; const kFrameDetailsSourcePositionIndex = 5;
const kFrameDetailsConstructCallIndex = 6; const kFrameDetailsConstructCallIndex = 6;
const kFrameDetailsDebuggerFrameIndex = 7; const kFrameDetailsAtReturnIndex = 7;
const kFrameDetailsFirstDynamicIndex = 8; const kFrameDetailsDebuggerFrameIndex = 8;
const kFrameDetailsFirstDynamicIndex = 9;
const kFrameDetailsNameIndex = 0; const kFrameDetailsNameIndex = 0;
const kFrameDetailsValueIndex = 1; const kFrameDetailsValueIndex = 1;
@ -1258,8 +1259,11 @@ const kFrameDetailsNameValueSize = 2;
* 4: Local count * 4: Local count
* 5: Source position * 5: Source position
* 6: Construct call * 6: Construct call
* 7: Is at return
* 8: Debugger frame
* Arguments name, value * Arguments name, value
* Locals name, value * Locals name, value
* Return value if any
* @param {number} break_id Current break id * @param {number} break_id Current break id
* @param {number} index Frame number * @param {number} index Frame number
* @constructor * @constructor
@ -1294,6 +1298,12 @@ FrameDetails.prototype.isConstructCall = function() {
} }
FrameDetails.prototype.isAtReturn = function() {
%CheckExecutionState(this.break_id_);
return this.details_[kFrameDetailsAtReturnIndex];
}
FrameDetails.prototype.isDebuggerFrame = function() { FrameDetails.prototype.isDebuggerFrame = function() {
%CheckExecutionState(this.break_id_); %CheckExecutionState(this.break_id_);
return this.details_[kFrameDetailsDebuggerFrameIndex]; return this.details_[kFrameDetailsDebuggerFrameIndex];
@ -1341,7 +1351,8 @@ FrameDetails.prototype.sourcePosition = function() {
FrameDetails.prototype.localName = function(index) { FrameDetails.prototype.localName = function(index) {
%CheckExecutionState(this.break_id_); %CheckExecutionState(this.break_id_);
if (index >= 0 && index < this.localCount()) { if (index >= 0 && index < this.localCount()) {
var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize var locals_offset = kFrameDetailsFirstDynamicIndex +
this.argumentCount() * kFrameDetailsNameValueSize
return this.details_[locals_offset + return this.details_[locals_offset +
index * kFrameDetailsNameValueSize + index * kFrameDetailsNameValueSize +
kFrameDetailsNameIndex] kFrameDetailsNameIndex]
@ -1352,7 +1363,8 @@ FrameDetails.prototype.localName = function(index) {
FrameDetails.prototype.localValue = function(index) { FrameDetails.prototype.localValue = function(index) {
%CheckExecutionState(this.break_id_); %CheckExecutionState(this.break_id_);
if (index >= 0 && index < this.localCount()) { if (index >= 0 && index < this.localCount()) {
var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize var locals_offset = kFrameDetailsFirstDynamicIndex +
this.argumentCount() * kFrameDetailsNameValueSize
return this.details_[locals_offset + return this.details_[locals_offset +
index * kFrameDetailsNameValueSize + index * kFrameDetailsNameValueSize +
kFrameDetailsValueIndex] kFrameDetailsValueIndex]
@ -1360,6 +1372,17 @@ FrameDetails.prototype.localValue = function(index) {
} }
FrameDetails.prototype.returnValue = function() {
%CheckExecutionState(this.break_id_);
var return_value_offset =
kFrameDetailsFirstDynamicIndex +
(this.argumentCount() + this.localCount()) * kFrameDetailsNameValueSize;
if (this.details_[kFrameDetailsAtReturnIndex]) {
return this.details_[return_value_offset];
}
}
FrameDetails.prototype.scopeCount = function() { FrameDetails.prototype.scopeCount = function() {
return %GetScopeCount(this.break_id_, this.frameId()); return %GetScopeCount(this.break_id_, this.frameId());
} }
@ -1412,6 +1435,11 @@ FrameMirror.prototype.isConstructCall = function() {
}; };
FrameMirror.prototype.isAtReturn = function() {
return this.details_.isAtReturn();
};
FrameMirror.prototype.isDebuggerFrame = function() { FrameMirror.prototype.isDebuggerFrame = function() {
return this.details_.isDebuggerFrame(); return this.details_.isDebuggerFrame();
}; };
@ -1447,6 +1475,11 @@ FrameMirror.prototype.localValue = function(index) {
}; };
FrameMirror.prototype.returnValue = function() {
return MakeMirror(this.details_.returnValue());
};
FrameMirror.prototype.sourcePosition = function() { FrameMirror.prototype.sourcePosition = function() {
return this.details_.sourcePosition(); return this.details_.sourcePosition();
}; };
@ -1574,6 +1607,11 @@ FrameMirror.prototype.invocationText = function() {
result += ')'; result += ')';
} }
if (this.isAtReturn()) {
result += ' returning ';
result += this.returnValue().toText();
}
return result; return result;
} }
@ -2267,6 +2305,10 @@ JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
content.script = this.serializeReference(func.script()); content.script = this.serializeReference(func.script());
} }
content.constructCall = mirror.isConstructCall(); content.constructCall = mirror.isConstructCall();
content.atReturn = mirror.isAtReturn();
if (mirror.isAtReturn()) {
content.returnValue = this.serializeReference(mirror.returnValue());
}
content.debuggerFrame = mirror.isDebuggerFrame(); content.debuggerFrame = mirror.isDebuggerFrame();
var x = new Array(mirror.argumentCount()); var x = new Array(mirror.argumentCount());
for (var i = 0; i < mirror.argumentCount(); i++) { for (var i = 0; i < mirror.argumentCount(); i++) {

11
deps/v8/src/profile-generator-inl.h

@ -130,6 +130,17 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
} }
} }
template<class Visitor>
void HeapEntriesMap::Apply(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value))
visitor->Apply(reinterpret_cast<HeapEntry*>(p->value));
}
}
} } // namespace v8::internal } } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING

793
deps/v8/src/profile-generator.cc

@ -29,11 +29,12 @@
#include "v8.h" #include "v8.h"
#include "global-handles.h" #include "global-handles.h"
#include "scopeinfo.h"
#include "top.h"
#include "zone-inl.h"
#include "profile-generator-inl.h" #include "profile-generator-inl.h"
#include "../include/v8-profiler.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
@ -811,6 +812,794 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
profiles_->AddPathToCurrentProfiles(entries); profiles_->AddPathToCurrentProfiles(entries);
} }
HeapGraphEdge::HeapGraphEdge(Type type,
const char* name,
HeapEntry* from,
HeapEntry* to)
: type_(type), name_(name), from_(from), to_(to) {
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
}
HeapGraphEdge::HeapGraphEdge(int index,
HeapEntry* from,
HeapEntry* to)
: type_(ELEMENT), index_(index), from_(from), to_(to) {
}
static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) {
delete *edge_ptr;
}
static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
delete *path_ptr;
}
HeapEntry::~HeapEntry() {
children_.Iterate(DeleteHeapGraphEdge);
retaining_paths_.Iterate(DeleteHeapGraphPath);
}
void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
HeapGraphEdge* edge =
new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry);
children_.Add(edge);
entry->retainers_.Add(edge);
}
void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry);
children_.Add(edge);
entry->retainers_.Add(edge);
}
void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
HeapGraphEdge* edge =
new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry);
children_.Add(edge);
entry->retainers_.Add(edge);
}
void HeapEntry::SetAutoIndexReference(HeapEntry* entry) {
SetElementReference(next_auto_index_++, entry);
}
int HeapEntry::TotalSize() {
return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
}
int HeapEntry::NonSharedTotalSize() {
return non_shared_total_size_ != kUnknownSize ?
non_shared_total_size_ : CalculateNonSharedTotalSize();
}
int HeapEntry::CalculateTotalSize() {
snapshot_->ClearPaint();
List<HeapEntry*> list(10);
list.Add(this);
total_size_ = self_size_;
this->PaintReachable();
while (!list.is_empty()) {
HeapEntry* entry = list.RemoveLast();
const int children_count = entry->children_.length();
for (int i = 0; i < children_count; ++i) {
HeapEntry* child = entry->children_[i]->to();
if (!child->painted_reachable()) {
list.Add(child);
child->PaintReachable();
total_size_ += child->self_size_;
}
}
}
return total_size_;
}
namespace {
class NonSharedSizeCalculator {
public:
NonSharedSizeCalculator()
: non_shared_total_size_(0) {
}
int non_shared_total_size() const { return non_shared_total_size_; }
void Apply(HeapEntry* entry) {
if (entry->painted_reachable()) {
non_shared_total_size_ += entry->self_size();
}
}
private:
int non_shared_total_size_;
};
} // namespace
int HeapEntry::CalculateNonSharedTotalSize() {
// To calculate non-shared total size, first we paint all reachable
// nodes in one color, then we paint all nodes reachable from other
// nodes with a different color. Then we consider only nodes painted
// with the first color for caclulating the total size.
snapshot_->ClearPaint();
List<HeapEntry*> list(10);
list.Add(this);
this->PaintReachable();
while (!list.is_empty()) {
HeapEntry* entry = list.RemoveLast();
const int children_count = entry->children_.length();
for (int i = 0; i < children_count; ++i) {
HeapEntry* child = entry->children_[i]->to();
if (!child->painted_reachable()) {
list.Add(child);
child->PaintReachable();
}
}
}
List<HeapEntry*> list2(10);
if (this != snapshot_->root()) {
list2.Add(snapshot_->root());
snapshot_->root()->PaintReachableFromOthers();
}
while (!list2.is_empty()) {
HeapEntry* entry = list2.RemoveLast();
const int children_count = entry->children_.length();
for (int i = 0; i < children_count; ++i) {
HeapEntry* child = entry->children_[i]->to();
if (child != this && child->not_painted_reachable_from_others()) {
list2.Add(child);
child->PaintReachableFromOthers();
}
}
}
NonSharedSizeCalculator calculator;
snapshot_->IterateEntries(&calculator);
return calculator.non_shared_total_size();
}
class CachedHeapGraphPath {
public:
CachedHeapGraphPath()
: nodes_(NodesMatch) { }
CachedHeapGraphPath(const CachedHeapGraphPath& src)
: nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()),
path_(src.path_.length() + 1) {
for (HashMap::Entry* p = src.nodes_.Start();
p != NULL;
p = src.nodes_.Next(p)) {
nodes_.Lookup(p->key, p->hash, true);
}
path_.AddAll(src.path_);
}
void Add(HeapGraphEdge* edge) {
nodes_.Lookup(edge->to(), Hash(edge->to()), true);
path_.Add(edge);
}
bool ContainsNode(HeapEntry* node) {
return nodes_.Lookup(node, Hash(node), false) != NULL;
}
const List<HeapGraphEdge*>* path() const { return &path_; }
private:
static uint32_t Hash(HeapEntry* entry) {
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry));
}
static bool NodesMatch(void* key1, void* key2) { return key1 == key2; }
HashMap nodes_;
List<HeapGraphEdge*> path_;
};
const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
if (retaining_paths_.length() == 0 && retainers_.length() != 0) {
CachedHeapGraphPath path;
FindRetainingPaths(this, &path);
}
return &retaining_paths_;
}
void HeapEntry::FindRetainingPaths(HeapEntry* node,
CachedHeapGraphPath* prev_path) {
for (int i = 0; i < node->retainers_.length(); ++i) {
HeapGraphEdge* ret_edge = node->retainers_[i];
if (prev_path->ContainsNode(ret_edge->from())) continue;
if (ret_edge->from() != snapshot_->root()) {
CachedHeapGraphPath path(*prev_path);
path.Add(ret_edge);
FindRetainingPaths(ret_edge->from(), &path);
} else {
HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
ret_path->Set(0, ret_edge);
retaining_paths_.Add(ret_path);
}
}
}
static void RemoveEdge(List<HeapGraphEdge*>* list, HeapGraphEdge* edge) {
for (int i = 0; i < list->length(); ) {
if (list->at(i) == edge) {
list->Remove(i);
return;
} else {
++i;
}
}
UNREACHABLE();
}
void HeapEntry::RemoveChild(HeapGraphEdge* edge) {
RemoveEdge(&children_, edge);
delete edge;
}
void HeapEntry::RemoveRetainer(HeapGraphEdge* edge) {
RemoveEdge(&retainers_, edge);
}
void HeapEntry::CutEdges() {
for (int i = 0; i < children_.length(); ++i) {
HeapGraphEdge* edge = children_[i];
edge->to()->RemoveRetainer(edge);
}
children_.Iterate(DeleteHeapGraphEdge);
children_.Clear();
for (int i = 0; i < retainers_.length(); ++i) {
HeapGraphEdge* edge = retainers_[i];
edge->from()->RemoveChild(edge);
}
retainers_.Clear();
}
void HeapEntry::Print(int max_depth, int indent) {
OS::Print("%6d %6d %6d", self_size_, TotalSize(), NonSharedTotalSize());
if (type_ != STRING) {
OS::Print("%s %.40s\n", TypeAsString(), name_);
} else {
OS::Print("\"");
const char* c = name_;
while (*c && (c - name_) <= 40) {
if (*c != '\n')
OS::Print("%c", *c);
else
OS::Print("\\n");
++c;
}
OS::Print("\"\n");
}
if (--max_depth == 0) return;
const int children_count = children_.length();
for (int i = 0; i < children_count; ++i) {
HeapGraphEdge* edge = children_[i];
switch (edge->type()) {
case HeapGraphEdge::CONTEXT_VARIABLE:
OS::Print(" %*c #%s: ", indent, ' ', edge->name());
break;
case HeapGraphEdge::ELEMENT:
OS::Print(" %*c %d: ", indent, ' ', edge->index());
break;
case HeapGraphEdge::PROPERTY:
OS::Print(" %*c %s: ", indent, ' ', edge->name());
break;
default:
OS::Print("!!! unknown edge type: %d ", edge->type());
}
edge->to()->Print(max_depth, indent + 2);
}
}
const char* HeapEntry::TypeAsString() {
switch (type_) {
case INTERNAL: return "/internal/";
case JS_OBJECT: return "/object/";
case CLOSURE: return "/closure/";
case STRING: return "/string/";
case CODE: return "/code/";
case ARRAY: return "/array/";
default: return "???";
}
}
HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
: path_(path.length() + 1) {
Add(NULL);
for (int i = path.length() - 1; i >= 0; --i) {
Add(path[i]);
}
}
void HeapGraphPath::Print() {
path_[0]->from()->Print(1, 0);
for (int i = 0; i < path_.length(); ++i) {
OS::Print(" -> ");
HeapGraphEdge* edge = path_[i];
switch (edge->type()) {
case HeapGraphEdge::CONTEXT_VARIABLE:
OS::Print("[#%s] ", edge->name());
break;
case HeapGraphEdge::ELEMENT:
OS::Print("[%d] ", edge->index());
break;
case HeapGraphEdge::PROPERTY:
OS::Print("[%s] ", edge->name());
break;
default:
OS::Print("!!! unknown edge type: %d ", edge->type());
}
edge->to()->Print(1, 0);
}
OS::Print("\n");
}
class IndexedReferencesExtractor : public ObjectVisitor {
public:
IndexedReferencesExtractor(HeapSnapshot* snapshot, HeapEntry* parent)
: snapshot_(snapshot),
parent_(parent) {
}
void VisitPointer(Object** o) {
if (!(*o)->IsHeapObject()) return;
HeapEntry* entry = snapshot_->GetEntry(HeapObject::cast(*o));
if (entry != NULL) {
parent_->SetAutoIndexReference(entry);
}
}
void VisitPointers(Object** start, Object** end) {
for (Object** p = start; p < end; p++) VisitPointer(p);
}
private:
HeapSnapshot* snapshot_;
HeapEntry* parent_;
};
HeapEntriesMap::HeapEntriesMap()
: entries_(HeapObjectsMatch) {
}
HeapEntriesMap::~HeapEntriesMap() {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value)) delete reinterpret_cast<HeapEntry*>(p->value);
}
}
void HeapEntriesMap::Alias(HeapObject* object, HeapEntry* entry) {
HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
if (cache_entry->value == NULL)
cache_entry->value = reinterpret_cast<void*>(
reinterpret_cast<intptr_t>(entry) | kAliasTag);
}
void HeapEntriesMap::Apply(void (HeapEntry::*Func)(void)) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value)) (reinterpret_cast<HeapEntry*>(p->value)->*Func)();
}
}
HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
return cache_entry != NULL ?
reinterpret_cast<HeapEntry*>(
reinterpret_cast<intptr_t>(cache_entry->value) & (~kAliasTag)) : NULL;
}
void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
ASSERT(cache_entry->value == NULL);
cache_entry->value = entry;
}
HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
const char* title,
unsigned uid)
: collection_(collection),
title_(title),
uid_(uid),
root_(this) {
}
void HeapSnapshot::ClearPaint() {
root_.ClearPaint();
entries_.Apply(&HeapEntry::ClearPaint);
}
HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
if (!obj->IsHeapObject()) return NULL;
HeapObject* object = HeapObject::cast(obj);
{
HeapEntry* existing = FindEntry(object);
if (existing != NULL) return existing;
}
// Add new entry.
if (object->IsJSFunction()) {
JSFunction* func = JSFunction::cast(object);
SharedFunctionInfo* shared = func->shared();
String* name = String::cast(shared->name())->length() > 0 ?
String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
} else if (object->IsJSObject()) {
return AddEntry(object,
HeapEntry::JS_OBJECT,
collection_->GetName(
JSObject::cast(object)->constructor_name()));
} else if (object->IsJSGlobalPropertyCell()) {
HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value());
// If GPC references an object that we have interest in, add the object.
// We don't store HeapEntries for GPCs. Instead, we make our hash map
// to point to object's HeapEntry by GPCs address.
if (value != NULL) AddEntryAlias(object, value);
return value;
} else if (object->IsString()) {
return AddEntry(object,
HeapEntry::STRING,
collection_->GetName(String::cast(object)));
} else if (object->IsCode()
|| object->IsSharedFunctionInfo()
|| object->IsScript()) {
return AddEntry(object, HeapEntry::CODE);
} else if (object->IsFixedArray()) {
return AddEntry(object, HeapEntry::ARRAY);
}
// No interest in this object.
return NULL;
}
void HeapSnapshot::SetClosureReference(HeapEntry* parent,
String* reference_name,
Object* child) {
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetClosureReference(
collection_->GetName(reference_name), child_entry);
}
}
void HeapSnapshot::SetElementReference(HeapEntry* parent,
int index,
Object* child) {
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetElementReference(index, child_entry);
}
}
void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
String* reference_name,
Object* child) {
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetPropertyReference(
collection_->GetName(reference_name), child_entry);
}
}
HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
HeapEntry::Type type,
const char* name) {
HeapEntry* entry = new HeapEntry(this,
type,
name,
GetObjectSize(object),
GetObjectSecurityToken(object));
entries_.Pair(object, entry);
// Detect, if this is a JS global object of the current context, and
// add it to snapshot's roots. There can be several JS global objects
// in a context.
if (object->IsJSGlobalProxy()) {
int global_security_token = GetGlobalSecurityToken();
int object_security_token =
collection_->token_enumerator()->GetTokenId(
Context::cast(
JSGlobalProxy::cast(object)->context())->security_token());
if (object_security_token == TokenEnumerator::kNoSecurityToken
|| object_security_token == global_security_token) {
HeapEntry* global_object_entry =
GetEntry(HeapObject::cast(object->map()->prototype()));
ASSERT(global_object_entry != NULL);
root_.SetAutoIndexReference(global_object_entry);
}
}
return entry;
}
namespace {
class EdgesCutter {
public:
explicit EdgesCutter(int global_security_token)
: global_security_token_(global_security_token) {
}
void Apply(HeapEntry* entry) {
if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken
&& entry->security_token_id() != global_security_token_) {
entry->CutEdges();
}
}
private:
const int global_security_token_;
};
} // namespace
void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
EdgesCutter cutter(GetGlobalSecurityToken());
entries_.Apply(&cutter);
}
int HeapSnapshot::GetGlobalSecurityToken() {
return collection_->token_enumerator()->GetTokenId(
Top::context()->global()->global_context()->security_token());
}
int HeapSnapshot::GetObjectSize(HeapObject* obj) {
return obj->IsJSObject() ?
CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
}
int HeapSnapshot::GetObjectSecurityToken(HeapObject* obj) {
if (obj->IsGlobalContext()) {
return collection_->token_enumerator()->GetTokenId(
Context::cast(obj)->security_token());
} else {
return TokenEnumerator::kNoSecurityToken;
}
}
int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
int size = obj->Size();
// If 'properties' and 'elements' are non-empty (thus, non-shared),
// take their size into account.
if (FixedArray::cast(obj->properties())->length() != 0) {
size += obj->properties()->Size();
}
if (FixedArray::cast(obj->elements())->length() != 0) {
size += obj->elements()->Size();
}
// For functions, also account non-empty context and literals sizes.
if (obj->IsJSFunction()) {
JSFunction* f = JSFunction::cast(obj);
if (f->unchecked_context()->IsContext()) {
size += f->context()->Size();
}
if (f->literals()->length() != 0) {
size += f->literals()->Size();
}
}
return size;
}
void HeapSnapshot::Print(int max_depth) {
root_.Print(max_depth, 0);
}
HeapSnapshotsCollection::HeapSnapshotsCollection()
: snapshots_uids_(HeapSnapshotsMatch),
token_enumerator_(new TokenEnumerator()) {
}
static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
delete *snapshot_ptr;
}
HeapSnapshotsCollection::~HeapSnapshotsCollection() {
delete token_enumerator_;
snapshots_.Iterate(DeleteHeapSnapshot);
}
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
unsigned uid) {
HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
snapshots_.Add(snapshot);
HashMap::Entry* entry =
snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
static_cast<uint32_t>(snapshot->uid()),
true);
ASSERT(entry->value == NULL);
entry->value = snapshot;
return snapshot;
}
HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
static_cast<uint32_t>(uid),
false);
return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
}
HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
: snapshot_(snapshot) {
}
void HeapSnapshotGenerator::GenerateSnapshot() {
AssertNoAllocation no_alloc;
// Iterate heap contents.
HeapIterator iterator;
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
ExtractReferences(obj);
}
snapshot_->CutObjectsFromForeignSecurityContexts();
}
void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
HeapEntry* entry = snapshot_->GetEntry(obj);
if (entry == NULL) return;
if (entry->visited()) return;
if (obj->IsJSObject()) {
JSObject* js_obj = JSObject::cast(obj);
ExtractClosureReferences(js_obj, entry);
ExtractPropertyReferences(js_obj, entry);
ExtractElementReferences(js_obj, entry);
snapshot_->SetPropertyReference(
entry, Heap::prototype_symbol(), js_obj->map()->prototype());
} else if (obj->IsJSGlobalPropertyCell()) {
JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj);
snapshot_->SetElementReference(entry, 0, cell->value());
} else if (obj->IsString()) {
if (obj->IsConsString()) {
ConsString* cs = ConsString::cast(obj);
snapshot_->SetElementReference(entry, 0, cs->first());
snapshot_->SetElementReference(entry, 1, cs->second());
}
} else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
IndexedReferencesExtractor refs_extractor(snapshot_, entry);
obj->Iterate(&refs_extractor);
} else if (obj->IsFixedArray()) {
IndexedReferencesExtractor refs_extractor(snapshot_, entry);
obj->Iterate(&refs_extractor);
}
entry->MarkAsVisited();
}
void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
HeapEntry* entry) {
if (js_obj->IsJSFunction()) {
HandleScope hs;
JSFunction* func = JSFunction::cast(js_obj);
Context* context = func->context();
ZoneScope zscope(DELETE_ON_EXIT);
ScopeInfo<ZoneListAllocationPolicy> scope_info(
context->closure()->shared()->code());
int locals_number = scope_info.NumberOfLocals();
for (int i = 0; i < locals_number; ++i) {
String* local_name = *scope_info.LocalName(i);
int idx = ScopeInfo<>::ContextSlotIndex(
context->closure()->shared()->code(), local_name, NULL);
if (idx >= 0 && idx < context->length()) {
snapshot_->SetClosureReference(entry, local_name, context->get(idx));
}
}
}
}
void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
HeapEntry* entry) {
if (js_obj->HasFastProperties()) {
DescriptorArray* descs = js_obj->map()->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
switch (descs->GetType(i)) {
case FIELD: {
int index = descs->GetFieldIndex(i);
snapshot_->SetPropertyReference(
entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
break;
}
case CONSTANT_FUNCTION:
snapshot_->SetPropertyReference(
entry, descs->GetKey(i), descs->GetConstantFunction(i));
break;
default: ;
}
}
} else {
StringDictionary* dictionary = js_obj->property_dictionary();
int length = dictionary->Capacity();
for (int i = 0; i < length; ++i) {
Object* k = dictionary->KeyAt(i);
if (dictionary->IsKey(k)) {
snapshot_->SetPropertyReference(
entry, String::cast(k), dictionary->ValueAt(i));
}
}
}
}
void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
HeapEntry* entry) {
if (js_obj->HasFastElements()) {
FixedArray* elements = FixedArray::cast(js_obj->elements());
int length = js_obj->IsJSArray() ?
Smi::cast(JSArray::cast(js_obj)->length())->value() :
elements->length();
for (int i = 0; i < length; ++i) {
if (!elements->get(i)->IsTheHole()) {
snapshot_->SetElementReference(entry, i, elements->get(i));
}
}
} else if (js_obj->HasDictionaryElements()) {
NumberDictionary* dictionary = js_obj->element_dictionary();
int length = dictionary->Capacity();
for (int i = 0; i < length; ++i) {
Object* k = dictionary->KeyAt(i);
if (dictionary->IsKey(k)) {
ASSERT(k->IsNumber());
uint32_t index = static_cast<uint32_t>(k->Number());
snapshot_->SetElementReference(entry, index, dictionary->ValueAt(i));
}
}
}
}
} } // namespace v8::internal } } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING

309
deps/v8/src/profile-generator.h

@ -31,6 +31,7 @@
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
#include "hashmap.h" #include "hashmap.h"
#include "../include/v8-profiler.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
@ -53,6 +54,8 @@ class TokenEnumerator {
List<bool> token_removed_; List<bool> token_removed_;
friend class TokenEnumeratorTester; friend class TokenEnumeratorTester;
DISALLOW_COPY_AND_ASSIGN(TokenEnumerator);
}; };
@ -357,6 +360,8 @@ class SampleRateCalculator {
unsigned measurements_count_; unsigned measurements_count_;
unsigned wall_time_query_countdown_; unsigned wall_time_query_countdown_;
double last_wall_time_; double last_wall_time_;
DISALLOW_COPY_AND_ASSIGN(SampleRateCalculator);
}; };
@ -416,6 +421,310 @@ class ProfileGenerator {
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator); DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
}; };
class HeapSnapshot;
class HeapEntry;
class HeapGraphEdge {
public:
enum Type {
CONTEXT_VARIABLE,
ELEMENT,
PROPERTY
};
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
HeapGraphEdge(int index, HeapEntry* from, HeapEntry* to);
Type type() const { return type_; }
int index() const {
ASSERT(type_ == ELEMENT);
return index_;
}
const char* name() const {
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
return name_;
}
HeapEntry* from() const { return from_; }
HeapEntry* to() const { return to_; }
private:
Type type_;
union {
int index_;
const char* name_;
};
HeapEntry* from_;
HeapEntry* to_;
DISALLOW_COPY_AND_ASSIGN(HeapGraphEdge);
};
class HeapGraphPath;
class CachedHeapGraphPath;
class HeapEntry {
public:
enum Type {
INTERNAL,
ARRAY,
STRING,
JS_OBJECT,
CODE,
CLOSURE
};
explicit HeapEntry(HeapSnapshot* snapshot)
: snapshot_(snapshot),
visited_(false),
type_(INTERNAL),
name_(""),
next_auto_index_(0),
self_size_(0),
security_token_id_(TokenEnumerator::kNoSecurityToken),
children_(1),
retainers_(0),
retaining_paths_(0),
total_size_(kUnknownSize),
non_shared_total_size_(kUnknownSize),
painted_(kUnpainted) { }
HeapEntry(HeapSnapshot* snapshot,
Type type,
const char* name,
int self_size,
int security_token_id)
: snapshot_(snapshot),
visited_(false),
type_(type),
name_(name),
next_auto_index_(1),
self_size_(self_size),
security_token_id_(security_token_id),
children_(4),
retainers_(4),
retaining_paths_(4),
total_size_(kUnknownSize),
non_shared_total_size_(kUnknownSize),
painted_(kUnpainted) { }
~HeapEntry();
bool visited() const { return visited_; }
Type type() const { return type_; }
const char* name() const { return name_; }
int self_size() const { return self_size_; }
int security_token_id() const { return security_token_id_; }
bool painted_reachable() { return painted_ == kPaintReachable; }
bool not_painted_reachable_from_others() {
return painted_ != kPaintReachableFromOthers;
}
const List<HeapGraphEdge*>* children() const { return &children_; }
const List<HeapGraphEdge*>* retainers() const { return &retainers_; }
const List<HeapGraphPath*>* GetRetainingPaths();
void ClearPaint() { painted_ = kUnpainted; }
void CutEdges();
void MarkAsVisited() { visited_ = true; }
void PaintReachable() {
ASSERT(painted_ == kUnpainted);
painted_ = kPaintReachable;
}
void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; }
void SetClosureReference(const char* name, HeapEntry* entry);
void SetElementReference(int index, HeapEntry* entry);
void SetPropertyReference(const char* name, HeapEntry* entry);
void SetAutoIndexReference(HeapEntry* entry);
int TotalSize();
int NonSharedTotalSize();
void Print(int max_depth, int indent);
private:
int CalculateTotalSize();
int CalculateNonSharedTotalSize();
void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path);
void RemoveChild(HeapGraphEdge* edge);
void RemoveRetainer(HeapGraphEdge* edge);
const char* TypeAsString();
HeapSnapshot* snapshot_;
bool visited_;
Type type_;
const char* name_;
int next_auto_index_;
int self_size_;
int security_token_id_;
List<HeapGraphEdge*> children_;
List<HeapGraphEdge*> retainers_;
List<HeapGraphPath*> retaining_paths_;
int total_size_;
int non_shared_total_size_;
int painted_;
static const int kUnknownSize = -1;
static const int kUnpainted = 0;
static const int kPaintReachable = 1;
static const int kPaintReachableFromOthers = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(HeapEntry);
};
class HeapGraphPath {
public:
HeapGraphPath()
: path_(8) { }
explicit HeapGraphPath(const List<HeapGraphEdge*>& path);
void Add(HeapGraphEdge* edge) { path_.Add(edge); }
void Set(int index, HeapGraphEdge* edge) { path_[index] = edge; }
const List<HeapGraphEdge*>* path() const { return &path_; }
void Print();
private:
List<HeapGraphEdge*> path_;
DISALLOW_COPY_AND_ASSIGN(HeapGraphPath);
};
class HeapEntriesMap {
public:
HeapEntriesMap();
~HeapEntriesMap();
void Alias(HeapObject* object, HeapEntry* entry);
void Apply(void (HeapEntry::*Func)(void));
template<class Visitor>
void Apply(Visitor* visitor);
HeapEntry* Map(HeapObject* object);
void Pair(HeapObject* object, HeapEntry* entry);
private:
INLINE(uint32_t Hash(HeapObject* object)) {
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
}
INLINE(static bool HeapObjectsMatch(void* key1, void* key2)) {
return key1 == key2;
}
INLINE(bool IsAlias(void* ptr)) {
return reinterpret_cast<intptr_t>(ptr) & kAliasTag;
}
static const intptr_t kAliasTag = 1;
HashMap entries_;
DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap);
};
class HeapSnapshotsCollection;
// HeapSnapshot represents a single heap snapshot. It is stored in
// HeapSnapshotsCollection, which is also a factory for
// HeapSnapshots. All HeapSnapshots share strings copied from JS heap
// to be able to return them even if they were collected.
// HeapSnapshotGenerator fills in a HeapSnapshot.
class HeapSnapshot {
public:
HeapSnapshot(HeapSnapshotsCollection* collection,
const char* title,
unsigned uid);
void ClearPaint();
void CutObjectsFromForeignSecurityContexts();
HeapEntry* GetEntry(Object* object);
void SetClosureReference(
HeapEntry* parent, String* reference_name, Object* child);
void SetElementReference(HeapEntry* parent, int index, Object* child);
void SetPropertyReference(
HeapEntry* parent, String* reference_name, Object* child);
INLINE(const char* title() const) { return title_; }
INLINE(unsigned uid() const) { return uid_; }
const HeapEntry* const_root() const { return &root_; }
HeapEntry* root() { return &root_; }
template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Apply(visitor); }
void Print(int max_depth);
private:
HeapEntry* AddEntry(HeapObject* object, HeapEntry::Type type) {
return AddEntry(object, type, "");
}
HeapEntry* AddEntry(
HeapObject* object, HeapEntry::Type type, const char* name);
void AddEntryAlias(HeapObject* object, HeapEntry* entry) {
entries_.Alias(object, entry);
}
HeapEntry* FindEntry(HeapObject* object) {
return entries_.Map(object);
}
int GetGlobalSecurityToken();
int GetObjectSecurityToken(HeapObject* obj);
static int GetObjectSize(HeapObject* obj);
static int CalculateNetworkSize(JSObject* obj);
HeapSnapshotsCollection* collection_;
const char* title_;
unsigned uid_;
HeapEntry root_;
// HeapObject* -> HeapEntry*
HeapEntriesMap entries_;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
};
class HeapSnapshotsCollection {
public:
HeapSnapshotsCollection();
~HeapSnapshotsCollection();
HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); }
TokenEnumerator* token_enumerator() { return token_enumerator_; }
private:
INLINE(static bool HeapSnapshotsMatch(void* key1, void* key2)) {
return key1 == key2;
}
List<HeapSnapshot*> snapshots_;
// uid -> HeapSnapshot*
HashMap snapshots_uids_;
StringsStorage names_;
TokenEnumerator* token_enumerator_;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsCollection);
};
class HeapSnapshotGenerator {
public:
explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
void GenerateSnapshot();
private:
void ExtractReferences(HeapObject* obj);
void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry);
HeapSnapshot* snapshot_;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator);
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING

55
deps/v8/src/runtime.cc

@ -8243,8 +8243,9 @@ static const int kFrameDetailsArgumentCountIndex = 3;
static const int kFrameDetailsLocalCountIndex = 4; static const int kFrameDetailsLocalCountIndex = 4;
static const int kFrameDetailsSourcePositionIndex = 5; static const int kFrameDetailsSourcePositionIndex = 5;
static const int kFrameDetailsConstructCallIndex = 6; static const int kFrameDetailsConstructCallIndex = 6;
static const int kFrameDetailsDebuggerFrameIndex = 7; static const int kFrameDetailsAtReturnIndex = 7;
static const int kFrameDetailsFirstDynamicIndex = 8; static const int kFrameDetailsDebuggerFrameIndex = 8;
static const int kFrameDetailsFirstDynamicIndex = 9;
// Return an array with frame details // Return an array with frame details
// args[0]: number: break id // args[0]: number: break id
@ -8258,9 +8259,11 @@ static const int kFrameDetailsFirstDynamicIndex = 8;
// 4: Local count // 4: Local count
// 5: Source position // 5: Source position
// 6: Constructor call // 6: Constructor call
// 7: Debugger frame // 7: Is at return
// 8: Debugger frame
// Arguments name, value // Arguments name, value
// Locals name, value // Locals name, value
// Return value if any
static Object* Runtime_GetFrameDetails(Arguments args) { static Object* Runtime_GetFrameDetails(Arguments args) {
HandleScope scope; HandleScope scope;
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
@ -8336,8 +8339,39 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
} }
} }
// Now advance to the arguments adapter frame (if any). If contains all // Check whether this frame is positioned at return.
// the provided parameters and int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
// If positioned just before return find the value to be returned and add it
// to the frame information.
Handle<Object> return_value = Factory::undefined_value();
if (at_return) {
StackFrameIterator it2;
Address internal_frame_sp = NULL;
while (!it2.done()) {
if (it2.frame()->is_internal()) {
internal_frame_sp = it2.frame()->sp();
} else {
if (it2.frame()->is_java_script()) {
if (it2.frame()->id() == it.frame()->id()) {
// The internal frame just before the JavaScript frame contains the
// value to return on top. A debug break at return will create an
// internal frame to store the return value (eax/rax/r0) before
// entering the debug break exit frame.
if (internal_frame_sp != NULL) {
return_value =
Handle<Object>(Memory::Object_at(internal_frame_sp));
break;
}
}
}
// Indicate that the previous frame was not an internal frame.
internal_frame_sp = NULL;
}
it2.Advance();
}
}
// Now advance to the arguments adapter frame (if any). It contains all // Now advance to the arguments adapter frame (if any). It contains all
// the provided parameters whereas the function frame always have the number // the provided parameters whereas the function frame always have the number
@ -8354,7 +8388,8 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
// Calculate the size of the result. // Calculate the size of the result.
int details_size = kFrameDetailsFirstDynamicIndex + int details_size = kFrameDetailsFirstDynamicIndex +
2 * (argument_count + info.NumberOfLocals()); 2 * (argument_count + info.NumberOfLocals()) +
(at_return ? 1 : 0);
Handle<FixedArray> details = Factory::NewFixedArray(details_size); Handle<FixedArray> details = Factory::NewFixedArray(details_size);
// Add the frame id. // Add the frame id.
@ -8380,6 +8415,9 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
// Add the constructor information. // Add the constructor information.
details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor)); details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
// Add the at return information.
details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
// Add information on whether this frame is invoked in the debugger context. // Add information on whether this frame is invoked in the debugger context.
details->set(kFrameDetailsDebuggerFrameIndex, details->set(kFrameDetailsDebuggerFrameIndex,
Heap::ToBoolean(*save->context() == *Debug::debug_context())); Heap::ToBoolean(*save->context() == *Debug::debug_context()));
@ -8409,6 +8447,11 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
details->set(details_index++, locals->get(i)); details->set(details_index++, locals->get(i));
} }
// Add the value being returned.
if (at_return) {
details->set(details_index++, *return_value);
}
// Add the receiver (same as in function frame). // Add the receiver (same as in function frame).
// THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
// THE FRAME ITERATOR TO WRAP THE RECEIVER. // THE FRAME ITERATOR TO WRAP THE RECEIVER.

27
deps/v8/src/spaces-inl.h

@ -152,6 +152,33 @@ uint32_t Page::GetRegionMaskForAddress(Address addr) {
} }
uint32_t Page::GetRegionMaskForSpan(Address start, int length_in_bytes) {
uint32_t result = 0;
if (length_in_bytes >= kPageSize) {
result = kAllRegionsDirtyMarks;
} else if (length_in_bytes > 0) {
int start_region = GetRegionNumberForAddress(start);
int end_region =
GetRegionNumberForAddress(start + length_in_bytes - kPointerSize);
uint32_t start_mask = (~0) << start_region;
uint32_t end_mask = ~((~1) << end_region);
result = start_mask & end_mask;
// if end_region < start_region, the mask is ored.
if (result == 0) result = start_mask | end_mask;
}
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
uint32_t expected = 0;
for (Address a = start; a < start + length_in_bytes; a += kPointerSize) {
expected |= GetRegionMaskForAddress(a);
}
ASSERT(expected == result);
}
#endif
return result;
}
void Page::MarkRegionDirty(Address address) { void Page::MarkRegionDirty(Address address) {
SetRegionMarks(GetRegionMarks() | GetRegionMaskForAddress(address)); SetRegionMarks(GetRegionMarks() | GetRegionMaskForAddress(address));
} }

1
deps/v8/src/spaces.h

@ -220,6 +220,7 @@ class Page {
inline void SetRegionMarks(uint32_t dirty); inline void SetRegionMarks(uint32_t dirty);
inline uint32_t GetRegionMaskForAddress(Address addr); inline uint32_t GetRegionMaskForAddress(Address addr);
inline uint32_t GetRegionMaskForSpan(Address start, int length_in_bytes);
inline int GetRegionNumberForAddress(Address addr); inline int GetRegionNumberForAddress(Address addr);
inline void MarkRegionDirty(Address addr); inline void MarkRegionDirty(Address addr);

8
deps/v8/src/v8-counters.h

@ -126,6 +126,14 @@ namespace internal {
SC(keyed_load_generic_lookup_cache, V8.KeyedLoadGenericLookupCache) \ SC(keyed_load_generic_lookup_cache, V8.KeyedLoadGenericLookupCache) \
SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \ SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \ SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
/* How is the generic keyed-call stub used? */ \
SC(keyed_call_generic_smi_fast, V8.KeyedCallGenericSmiFast) \
SC(keyed_call_generic_smi_dict, V8.KeyedCallGenericSmiDict) \
SC(keyed_call_generic_lookup_cache, V8.KeyedCallGenericLookupCache) \
SC(keyed_call_generic_lookup_dict, V8.KeyedCallGenericLookupDict) \
SC(keyed_call_generic_value_type, V8.KeyedCallGenericValueType) \
SC(keyed_call_generic_slow, V8.KeyedCallGenericSlow) \
SC(keyed_call_generic_slow_load, V8.KeyedCallGenericSlowLoad) \
/* Count how much the monomorphic keyed-load stubs are hit. */ \ /* Count how much the monomorphic keyed-load stubs are hit. */ \
SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \ SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \
SC(keyed_load_string_length, V8.KeyedLoadStringLength) \ SC(keyed_load_string_length, V8.KeyedLoadStringLength) \

2
deps/v8/src/version.cc

@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script. // cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2 #define MAJOR_VERSION 2
#define MINOR_VERSION 2 #define MINOR_VERSION 2
#define BUILD_NUMBER 16 #define BUILD_NUMBER 17
#define PATCH_LEVEL 0 #define PATCH_LEVEL 0
#define CANDIDATE_VERSION false #define CANDIDATE_VERSION false

79
deps/v8/src/x64/codegen-x64.cc

@ -6894,7 +6894,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
smi_value, smi_value,
overwrite_mode); overwrite_mode);
} }
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
__ SmiAddConstant(operand->reg(), __ SmiAddConstant(operand->reg(),
operand->reg(), operand->reg(),
smi_value, smi_value,
@ -6915,7 +6916,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
DeferredCode* deferred = new DeferredInlineSmiSub(operand->reg(), DeferredCode* deferred = new DeferredInlineSmiSub(operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
// A smi currently fits in a 32-bit Immediate. // A smi currently fits in a 32-bit Immediate.
__ SmiSubConstant(operand->reg(), __ SmiSubConstant(operand->reg(),
operand->reg(), operand->reg(),
@ -6944,7 +6946,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
operand->reg(), operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
__ SmiShiftArithmeticRightConstant(operand->reg(), __ SmiShiftArithmeticRightConstant(operand->reg(),
operand->reg(), operand->reg(),
shift_value); shift_value);
@ -6971,7 +6974,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
operand->reg(), operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
__ SmiShiftLogicalRightConstant(answer.reg(), __ SmiShiftLogicalRightConstant(answer.reg(),
operand->reg(), operand->reg(),
shift_value, shift_value,
@ -7003,12 +7007,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
smi_value, smi_value,
operand->reg(), operand->reg(),
overwrite_mode); overwrite_mode);
if (!operand->type_info().IsSmi()) { JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
Condition is_smi = masm_->CheckSmi(operand->reg()); deferred);
deferred->Branch(NegateCondition(is_smi));
} else if (FLAG_debug_code) {
__ AbortIfNotSmi(operand->reg());
}
__ Move(answer.reg(), smi_value); __ Move(answer.reg(), smi_value);
__ SmiShiftLeft(answer.reg(), answer.reg(), operand->reg()); __ SmiShiftLeft(answer.reg(), answer.reg(), operand->reg());
@ -7029,7 +7029,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
operand->reg(), operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
deferred->BindExit(); deferred->BindExit();
answer = *operand; answer = *operand;
} else { } else {
@ -7042,7 +7043,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
operand->reg(), operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
__ SmiShiftLeftConstant(answer.reg(), __ SmiShiftLeftConstant(answer.reg(),
operand->reg(), operand->reg(),
shift_value); shift_value);
@ -7068,7 +7070,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
operand->reg(), operand->reg(),
smi_value, smi_value,
overwrite_mode); overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label()); JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
deferred);
if (op == Token::BIT_AND) { if (op == Token::BIT_AND) {
__ SmiAndConstant(operand->reg(), operand->reg(), smi_value); __ SmiAndConstant(operand->reg(), operand->reg(), smi_value);
} else if (op == Token::BIT_XOR) { } else if (op == Token::BIT_XOR) {
@ -7133,6 +7136,37 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
} }
void CodeGenerator::JumpIfNotSmiUsingTypeInfo(Register reg,
TypeInfo type,
DeferredCode* deferred) {
if (!type.IsSmi()) {
__ JumpIfNotSmi(reg, deferred->entry_label());
}
if (FLAG_debug_code) {
__ AbortIfNotSmi(reg);
}
}
void CodeGenerator::JumpIfNotBothSmiUsingTypeInfo(Register left,
Register right,
TypeInfo left_info,
TypeInfo right_info,
DeferredCode* deferred) {
if (!left_info.IsSmi() && !right_info.IsSmi()) {
__ JumpIfNotBothSmi(left, right, deferred->entry_label());
} else if (!left_info.IsSmi()) {
__ JumpIfNotSmi(left, deferred->entry_label());
} else if (!right_info.IsSmi()) {
__ JumpIfNotSmi(right, deferred->entry_label());
}
if (FLAG_debug_code) {
__ AbortIfNotSmi(left);
__ AbortIfNotSmi(right);
}
}
// Implements a binary operation using a deferred code object and some // Implements a binary operation using a deferred code object and some
// inline code to operate on smis quickly. // inline code to operate on smis quickly.
Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr, Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
@ -7142,9 +7176,6 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
// Copy the type info because left and right may be overwritten. // Copy the type info because left and right may be overwritten.
TypeInfo left_type_info = left->type_info(); TypeInfo left_type_info = left->type_info();
TypeInfo right_type_info = right->type_info(); TypeInfo right_type_info = right->type_info();
USE(left_type_info);
USE(right_type_info);
// TODO(X64): Use type information in calculations.
Token::Value op = expr->op(); Token::Value op = expr->op();
Result answer; Result answer;
// Special handling of div and mod because they use fixed registers. // Special handling of div and mod because they use fixed registers.
@ -7221,7 +7252,8 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
left->reg(), left->reg(),
right->reg(), right->reg(),
overwrite_mode); overwrite_mode);
__ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label()); JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(),
left_type_info, right_type_info, deferred);
if (op == Token::DIV) { if (op == Token::DIV) {
__ SmiDiv(rax, left->reg(), right->reg(), deferred->entry_label()); __ SmiDiv(rax, left->reg(), right->reg(), deferred->entry_label());
@ -7303,7 +7335,8 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
} }
} }
} else { } else {
__ JumpIfNotBothSmi(left->reg(), rcx, deferred->entry_label()); JumpIfNotBothSmiUsingTypeInfo(left->reg(), rcx,
left_type_info, right_type_info, deferred);
} }
__ bind(&do_op); __ bind(&do_op);
@ -7351,7 +7384,8 @@ Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
left->reg(), left->reg(),
right->reg(), right->reg(),
overwrite_mode); overwrite_mode);
__ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label()); JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(),
left_type_info, right_type_info, deferred);
switch (op) { switch (op) {
case Token::ADD: case Token::ADD:
@ -8155,14 +8189,15 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ movl(rcx, rdx); __ movl(rcx, rdx);
__ movl(rax, rdx); __ movl(rax, rdx);
__ movl(rdi, rdx); __ movl(rdi, rdx);
__ sarl(rdx, Immediate(8)); __ shrl(rdx, Immediate(8));
__ sarl(rcx, Immediate(16)); __ shrl(rcx, Immediate(16));
__ sarl(rax, Immediate(24)); __ shrl(rax, Immediate(24));
__ xorl(rcx, rdx); __ xorl(rcx, rdx);
__ xorl(rax, rdi); __ xorl(rax, rdi);
__ xorl(rcx, rax); __ xorl(rcx, rax);
ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize)); ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
__ andl(rcx, Immediate(TranscendentalCache::kCacheSize - 1)); __ andl(rcx, Immediate(TranscendentalCache::kCacheSize - 1));
// ST[0] == double value. // ST[0] == double value.
// rbx = bits of double value. // rbx = bits of double value.
// rcx = TranscendentalCache::hash(double value). // rcx = TranscendentalCache::hash(double value).

16
deps/v8/src/x64/codegen-x64.h

@ -477,6 +477,22 @@ class CodeGenerator: public AstVisitor {
void GenericBinaryOperation(BinaryOperation* expr, void GenericBinaryOperation(BinaryOperation* expr,
OverwriteMode overwrite_mode); OverwriteMode overwrite_mode);
// Emits code sequence that jumps to deferred code if the input
// is not a smi. Cannot be in MacroAssembler because it takes
// advantage of TypeInfo to skip unneeded checks.
void JumpIfNotSmiUsingTypeInfo(Register reg,
TypeInfo type,
DeferredCode* deferred);
// Emits code sequence that jumps to deferred code if the inputs
// are not both smis. Cannot be in MacroAssembler because it takes
// advantage of TypeInfo to skip unneeded checks.
void JumpIfNotBothSmiUsingTypeInfo(Register left,
Register right,
TypeInfo left_info,
TypeInfo right_info,
DeferredCode* deferred);
// If possible, combine two constant smi values using op to produce // If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time. // a smi result, and push it on the virtual frame, all at compile time.
// Returns true if it succeeds. Otherwise it has no effect. // Returns true if it succeeds. Otherwise it has no effect.

6
deps/v8/src/x64/full-codegen-x64.cc

@ -187,12 +187,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) {
{ Comment cmnt(masm_, "[ return <undefined>;"); { Comment cmnt(masm_, "[ return <undefined>;");
// Emit a 'return undefined' in case control fell off the end of the body. // Emit a 'return undefined' in case control fell off the end of the body.
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex); __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
EmitReturnSequence(function()->end_position()); EmitReturnSequence();
} }
} }
void FullCodeGenerator::EmitReturnSequence(int position) { void FullCodeGenerator::EmitReturnSequence() {
Comment cmnt(masm_, "[ Return sequence"); Comment cmnt(masm_, "[ Return sequence");
if (return_label_.is_bound()) { if (return_label_.is_bound()) {
__ jmp(&return_label_); __ jmp(&return_label_);
@ -207,7 +207,7 @@ void FullCodeGenerator::EmitReturnSequence(int position) {
Label check_exit_codesize; Label check_exit_codesize;
masm_->bind(&check_exit_codesize); masm_->bind(&check_exit_codesize);
#endif #endif
CodeGenerator::RecordPositions(masm_, position); CodeGenerator::RecordPositions(masm_, function()->end_position());
__ RecordJSReturn(); __ RecordJSReturn();
// Do not use the leave instruction here because it is too short to // Do not use the leave instruction here because it is too short to
// patch with the code required by the debugger. // patch with the code required by the debugger.

2
deps/v8/test/cctest/test-heap.cc

@ -960,6 +960,8 @@ TEST(Regression39128) {
TEST(TestCodeFlushing) { TEST(TestCodeFlushing) {
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
// If we do not flush code this test is invalid.
if (!FLAG_flush_code) return;
InitializeVM(); InitializeVM();
v8::HandleScope scope; v8::HandleScope scope;
const char* source = "function foo() {" const char* source = "function foo() {"

163
deps/v8/test/mjsunit/debug-return-value.js

@ -0,0 +1,163 @@
// Copyright 2010 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.
// Flags: --expose-debug-as debug
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug
function ParsedResponse(json) {
this.response_ = eval('(' + json + ')');
this.refs_ = [];
if (this.response_.refs) {
for (var i = 0; i < this.response_.refs.length; i++) {
this.refs_[this.response_.refs[i].handle] = this.response_.refs[i];
}
}
}
ParsedResponse.prototype.response = function() {
return this.response_;
}
ParsedResponse.prototype.body = function() {
return this.response_.body;
}
ParsedResponse.prototype.running = function() {
return this.response_.running;
}
ParsedResponse.prototype.lookup = function(handle) {
return this.refs_[handle];
}
listener_complete = false;
exception = false;
break_count = 0;
expected_return_value = 0;
debugger_source_position = 0;
// Listener which expects to do four steps to reach returning from the function.
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break)
{
break_count++;
if (break_count < 4) {
assertFalse(exec_state.frame(0).isAtReturn())
switch (break_count) {
case 1:
// Collect the position of the debugger statement.
debugger_source_position = exec_state.frame(0).sourcePosition();
break;
case 2:
// Position now at the if statement.
assertEquals(debugger_source_position + 10,
exec_state.frame(0).sourcePosition());
break;
case 3:
// Position now at either of the returns.
if (expected_return_value == 1) {
assertEquals(debugger_source_position + 19,
exec_state.frame(0).sourcePosition());
} else {
assertEquals(debugger_source_position + 38,
exec_state.frame(0).sourcePosition());
}
break;
default:
fail("Unexpected");
}
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} else {
// Position at the end of the function.
assertEquals(debugger_source_position + 51,
exec_state.frame(0).sourcePosition());
// Just about to return from the function.
assertTrue(exec_state.frame(0).isAtReturn())
assertEquals(expected_return_value,
exec_state.frame(0).returnValue().value());
// Check the same using the JSON commands.
var dcp = exec_state.debugCommandProcessor(false);
var request = '{"seq":0,"type":"request","command":"backtrace"}';
var resp = dcp.processDebugJSONRequest(request);
response = new ParsedResponse(resp);
frames = response.body().frames;
assertTrue(frames[0].atReturn);
assertEquals(expected_return_value,
response.lookup(frames[0].returnValue.ref).value);
listener_complete = true;
}
}
} catch (e) {
exception = e
};
};
// Add the debug event listener.
Debug.setListener(listener);
// Four steps from the debugger statement in this function will position us at
// the function return.
// 0 1 2 3 4 5
// 0123456789012345678901234567890123456789012345678901
function f(x) {debugger; if (x) { return 1; } else { return 2; } };
// Call f expecting different return values.
break_count = 0;
expected_return_value = 2;
listener_complete = false;
f();
assertFalse(exception, "exception in listener")
assertTrue(listener_complete);
assertEquals(4, break_count);
break_count = 0;
expected_return_value = 1;
listener_complete = false;
f(true);
assertFalse(exception, "exception in listener")
assertTrue(listener_complete);
assertEquals(4, break_count);
break_count = 0;
expected_return_value = 2;
listener_complete = false;
f(false);
assertFalse(exception, "exception in listener")
assertTrue(listener_complete);
assertEquals(4, break_count);

96
deps/v8/test/mjsunit/keyed-call-generic.js

@ -0,0 +1,96 @@
// Copyright 2010 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.
// A test for keyed call ICs with a mix of smi and string keys.
function testOne(receiver, key, result) {
for(var i = 0; i != 10; i++ ) {
assertEquals(result, receiver[key]());
}
}
function testMany(receiver, keys, results) {
for (var i = 0; i != 10; i++) {
for (var k = 0; k != keys.length; k++) {
assertEquals(results[k], receiver[keys[k]]());
}
}
}
var toStringNonSymbol = 'to';
toStringNonSymbol += 'String';
function TypeOfThis() { return typeof this; }
Number.prototype.square = function() { return this * this; }
Number.prototype.power4 = function() { return this.square().square(); }
Number.prototype.type = TypeOfThis;
String.prototype.type = TypeOfThis;
Boolean.prototype.type = TypeOfThis;
// Use a non-symbol key to force inline cache to generic case.
testOne(0, toStringNonSymbol, '0');
testOne(1, 'toString', '1');
testOne('1', 'toString', '1');
testOne(1.0, 'toString', '1');
testOne(1, 'type', 'object');
testOne(2.3, 'type', 'object');
testOne('x', 'type', 'object');
testOne(true, 'type', 'object');
testOne(false, 'type', 'object');
testOne(2, 'square', 4);
testOne(2, 'power4', 16);
function zero () { return 0; }
function one () { return 1; }
function two () { return 2; }
var fixed_array = [zero, one, two];
var dict_array = [ zero, one, two ];
dict_array[100000] = 1;
var fast_prop = { zero: zero, one: one, two: two };
var normal_prop = { zero: zero, one: one, two: two };
normal_prop.x = 0;
delete normal_prop.x;
var first3num = [0, 1, 2];
var first3str = ['zero', 'one', 'two'];
// Use a non-symbol key to force inline cache to generic case.
testMany('123', [toStringNonSymbol, 'charAt', 'charCodeAt'], ['123', '1', 49]);
testMany(fixed_array, first3num, first3num);
testMany(dict_array, first3num, first3num);
testMany(fast_prop, first3str, first3num);
testMany(normal_prop, first3str, first3num);
Loading…
Cancel
Save