Upgrade V8 to 2.3.2
This commit is contained in:
parent
07ab34cd58
commit
e4eeaa7fbc
18
deps/v8/ChangeLog
vendored
18
deps/v8/ChangeLog
vendored
@ -1,3 +1,21 @@
|
|||||||
|
2010-07-21: Version 2.3.2
|
||||||
|
|
||||||
|
Fixed compiler warnings when building with LLVM.
|
||||||
|
|
||||||
|
Fixed a bug with for-in applied to strings (issue 785).
|
||||||
|
|
||||||
|
Performance improvements on all platforms.
|
||||||
|
|
||||||
|
2010-07-19: Version 2.3.1
|
||||||
|
|
||||||
|
Fixed compilation and linking with V8_INTERPRETED_REGEXP flag.
|
||||||
|
|
||||||
|
Fixed bug related to code flushing while compiling a lazy
|
||||||
|
compilable function (issue http://crbug.com/49099).
|
||||||
|
|
||||||
|
Performance improvements on all platforms.
|
||||||
|
|
||||||
|
|
||||||
2010-07-15: Version 2.3.0
|
2010-07-15: Version 2.3.0
|
||||||
|
|
||||||
Added ES5 Object.seal and Object.isSealed.
|
Added ES5 Object.seal and Object.isSealed.
|
||||||
|
1
deps/v8/SConstruct
vendored
1
deps/v8/SConstruct
vendored
@ -300,6 +300,7 @@ V8_EXTRA_FLAGS = {
|
|||||||
'gcc': {
|
'gcc': {
|
||||||
'all': {
|
'all': {
|
||||||
'WARNINGFLAGS': ['-Wall',
|
'WARNINGFLAGS': ['-Wall',
|
||||||
|
'-Werror',
|
||||||
'-W',
|
'-W',
|
||||||
'-Wno-unused-parameter',
|
'-Wno-unused-parameter',
|
||||||
'-Wnon-virtual-dtor']
|
'-Wnon-virtual-dtor']
|
||||||
|
21
deps/v8/include/v8-profiler.h
vendored
21
deps/v8/include/v8-profiler.h
vendored
@ -258,6 +258,12 @@ class V8EXPORT HeapGraphNode {
|
|||||||
*/
|
*/
|
||||||
Handle<String> GetName() const;
|
Handle<String> GetName() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns node id. For the same heap object, the id remains the same
|
||||||
|
* across all snapshots.
|
||||||
|
*/
|
||||||
|
uint64_t GetId() const;
|
||||||
|
|
||||||
/** Returns node's own size, in bytes. */
|
/** Returns node's own size, in bytes. */
|
||||||
int GetSelfSize() const;
|
int GetSelfSize() const;
|
||||||
|
|
||||||
@ -290,6 +296,16 @@ class V8EXPORT HeapGraphNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class V8EXPORT HeapSnapshotsDiff {
|
||||||
|
public:
|
||||||
|
/** Returns the root node for added nodes. */
|
||||||
|
const HeapGraphNode* GetAdditionsRoot() const;
|
||||||
|
|
||||||
|
/** Returns the root node for deleted nodes. */
|
||||||
|
const HeapGraphNode* GetDeletionsRoot() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HeapSnapshots record the state of the JS heap at some moment.
|
* HeapSnapshots record the state of the JS heap at some moment.
|
||||||
*/
|
*/
|
||||||
@ -302,7 +318,10 @@ class V8EXPORT HeapSnapshot {
|
|||||||
Handle<String> GetTitle() const;
|
Handle<String> GetTitle() const;
|
||||||
|
|
||||||
/** Returns the root node of the heap graph. */
|
/** Returns the root node of the heap graph. */
|
||||||
const HeapGraphNode* GetHead() const;
|
const HeapGraphNode* GetRoot() const;
|
||||||
|
|
||||||
|
/** Returns a diff between this snapshot and another one. */
|
||||||
|
const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
9
deps/v8/include/v8.h
vendored
9
deps/v8/include/v8.h
vendored
@ -137,6 +137,9 @@ class Top;
|
|||||||
/**
|
/**
|
||||||
* A weak reference callback function.
|
* A weak reference callback function.
|
||||||
*
|
*
|
||||||
|
* This callback should either explicitly invoke Dispose on |object| if
|
||||||
|
* V8 wrapper is not needed anymore, or 'revive' it by invocation of MakeWeak.
|
||||||
|
*
|
||||||
* \param object the weak global object to be reclaimed by the garbage collector
|
* \param object the weak global object to be reclaimed by the garbage collector
|
||||||
* \param parameter the value passed in when making the weak global object
|
* \param parameter the value passed in when making the weak global object
|
||||||
*/
|
*/
|
||||||
@ -146,9 +149,9 @@ typedef void (*WeakReferenceCallback)(Persistent<Value> object,
|
|||||||
|
|
||||||
// --- H a n d l e s ---
|
// --- H a n d l e s ---
|
||||||
|
|
||||||
#define TYPE_CHECK(T, S) \
|
#define TYPE_CHECK(T, S) \
|
||||||
while (false) { \
|
while (false) { \
|
||||||
*(static_cast<T**>(0)) = static_cast<S*>(0); \
|
*(static_cast<T* volatile*>(0)) = static_cast<S*>(0); \
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
36
deps/v8/src/api.cc
vendored
36
deps/v8/src/api.cc
vendored
@ -4561,6 +4561,12 @@ Handle<String> HeapGraphNode::GetName() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t HeapGraphNode::GetId() const {
|
||||||
|
IsDeadCheck("v8::HeapGraphNode::GetId");
|
||||||
|
return reinterpret_cast<const i::HeapEntry*>(this)->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int HeapGraphNode::GetSelfSize() const {
|
int HeapGraphNode::GetSelfSize() const {
|
||||||
IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
|
IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
|
||||||
return reinterpret_cast<const i::HeapEntry*>(this)->self_size();
|
return reinterpret_cast<const i::HeapEntry*>(this)->self_size();
|
||||||
@ -4624,6 +4630,22 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const {
|
||||||
|
IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot");
|
||||||
|
const i::HeapSnapshotsDiff* diff =
|
||||||
|
reinterpret_cast<const i::HeapSnapshotsDiff*>(this);
|
||||||
|
return reinterpret_cast<const HeapGraphNode*>(diff->additions_root());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const HeapGraphNode* HeapSnapshotsDiff::GetDeletionsRoot() const {
|
||||||
|
IsDeadCheck("v8::HeapSnapshotsDiff::GetDeletionsRoot");
|
||||||
|
const i::HeapSnapshotsDiff* diff =
|
||||||
|
reinterpret_cast<const i::HeapSnapshotsDiff*>(this);
|
||||||
|
return reinterpret_cast<const HeapGraphNode*>(diff->deletions_root());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned HeapSnapshot::GetUid() const {
|
unsigned HeapSnapshot::GetUid() const {
|
||||||
IsDeadCheck("v8::HeapSnapshot::GetUid");
|
IsDeadCheck("v8::HeapSnapshot::GetUid");
|
||||||
return reinterpret_cast<const i::HeapSnapshot*>(this)->uid();
|
return reinterpret_cast<const i::HeapSnapshot*>(this)->uid();
|
||||||
@ -4639,7 +4661,7 @@ Handle<String> HeapSnapshot::GetTitle() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const HeapGraphNode* HeapSnapshot::GetHead() const {
|
const HeapGraphNode* HeapSnapshot::GetRoot() const {
|
||||||
IsDeadCheck("v8::HeapSnapshot::GetHead");
|
IsDeadCheck("v8::HeapSnapshot::GetHead");
|
||||||
const i::HeapSnapshot* snapshot =
|
const i::HeapSnapshot* snapshot =
|
||||||
reinterpret_cast<const i::HeapSnapshot*>(this);
|
reinterpret_cast<const i::HeapSnapshot*>(this);
|
||||||
@ -4647,6 +4669,18 @@ const HeapGraphNode* HeapSnapshot::GetHead() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const HeapSnapshotsDiff* HeapSnapshot::CompareWith(
|
||||||
|
const HeapSnapshot* snapshot) const {
|
||||||
|
IsDeadCheck("v8::HeapSnapshot::CompareWith");
|
||||||
|
i::HeapSnapshot* snapshot1 = const_cast<i::HeapSnapshot*>(
|
||||||
|
reinterpret_cast<const i::HeapSnapshot*>(this));
|
||||||
|
i::HeapSnapshot* snapshot2 = const_cast<i::HeapSnapshot*>(
|
||||||
|
reinterpret_cast<const i::HeapSnapshot*>(snapshot));
|
||||||
|
return reinterpret_cast<const HeapSnapshotsDiff*>(
|
||||||
|
snapshot1->CompareWith(snapshot2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int HeapProfiler::GetSnapshotsCount() {
|
int HeapProfiler::GetSnapshotsCount() {
|
||||||
IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
|
IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
|
||||||
return i::HeapProfiler::GetSnapshotsCount();
|
return i::HeapProfiler::GetSnapshotsCount();
|
||||||
|
24
deps/v8/src/arm/assembler-arm.cc
vendored
24
deps/v8/src/arm/assembler-arm.cc
vendored
@ -1192,6 +1192,30 @@ void Assembler::clz(Register dst, Register src, Condition cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Saturating instructions.
|
||||||
|
|
||||||
|
// Unsigned saturate.
|
||||||
|
void Assembler::usat(Register dst,
|
||||||
|
int satpos,
|
||||||
|
const Operand& src,
|
||||||
|
Condition cond) {
|
||||||
|
// v6 and above.
|
||||||
|
ASSERT(CpuFeatures::IsSupported(ARMv7));
|
||||||
|
ASSERT(!dst.is(pc) && !src.rm_.is(pc));
|
||||||
|
ASSERT((satpos >= 0) && (satpos <= 31));
|
||||||
|
ASSERT((src.shift_op_ == ASR) || (src.shift_op_ == LSL));
|
||||||
|
ASSERT(src.rs_.is(no_reg));
|
||||||
|
|
||||||
|
int sh = 0;
|
||||||
|
if (src.shift_op_ == ASR) {
|
||||||
|
sh = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(cond | 0x6*B24 | 0xe*B20 | satpos*B16 | dst.code()*B12 |
|
||||||
|
src.shift_imm_*B7 | sh*B6 | 0x1*B4 | src.rm_.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Bitfield manipulation instructions.
|
// Bitfield manipulation instructions.
|
||||||
|
|
||||||
// Unsigned bit field extract.
|
// Unsigned bit field extract.
|
||||||
|
21
deps/v8/src/arm/assembler-arm.h
vendored
21
deps/v8/src/arm/assembler-arm.h
vendored
@ -445,6 +445,8 @@ class Operand BASE_EMBEDDED {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Register rm() const { return rm_; }
|
Register rm() const { return rm_; }
|
||||||
|
Register rs() const { return rs_; }
|
||||||
|
ShiftOp shift_op() const { return shift_op_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register rm_;
|
Register rm_;
|
||||||
@ -834,6 +836,25 @@ class Assembler : public Malloced {
|
|||||||
|
|
||||||
void clz(Register dst, Register src, Condition cond = al); // v5 and above
|
void clz(Register dst, Register src, Condition cond = al); // v5 and above
|
||||||
|
|
||||||
|
// Saturating instructions. v6 and above.
|
||||||
|
|
||||||
|
// Unsigned saturate.
|
||||||
|
//
|
||||||
|
// Saturate an optionally shifted signed value to an unsigned range.
|
||||||
|
//
|
||||||
|
// usat dst, #satpos, src
|
||||||
|
// usat dst, #satpos, src, lsl #sh
|
||||||
|
// usat dst, #satpos, src, asr #sh
|
||||||
|
//
|
||||||
|
// Register dst will contain:
|
||||||
|
//
|
||||||
|
// 0, if s < 0
|
||||||
|
// (1 << satpos) - 1, if s > ((1 << satpos) - 1)
|
||||||
|
// s, otherwise
|
||||||
|
//
|
||||||
|
// where s is the contents of src after shifting (if used.)
|
||||||
|
void usat(Register dst, int satpos, const Operand& src, Condition cond = al);
|
||||||
|
|
||||||
// Bitfield manipulation instructions. v7 and above.
|
// Bitfield manipulation instructions. v7 and above.
|
||||||
|
|
||||||
void ubfx(Register dst, Register src, int lsb, int width,
|
void ubfx(Register dst, Register src, int lsb, int width,
|
||||||
|
18
deps/v8/src/arm/codegen-arm.cc
vendored
18
deps/v8/src/arm/codegen-arm.cc
vendored
@ -4760,6 +4760,24 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
// This generates a fast version of:
|
||||||
|
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
|
||||||
|
// typeof(arg) == function).
|
||||||
|
// It includes undetectable objects (as opposed to IsObject).
|
||||||
|
ASSERT(args->length() == 1);
|
||||||
|
Load(args->at(0));
|
||||||
|
Register value = frame_->PopToRegister();
|
||||||
|
__ tst(value, Operand(kSmiTagMask));
|
||||||
|
false_target()->Branch(eq);
|
||||||
|
// Check that this is an object.
|
||||||
|
__ ldr(value, FieldMemOperand(value, HeapObject::kMapOffset));
|
||||||
|
__ ldrb(value, FieldMemOperand(value, Map::kInstanceTypeOffset));
|
||||||
|
__ cmp(value, Operand(FIRST_JS_OBJECT_TYPE));
|
||||||
|
cc_reg_ = ge;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
||||||
// This generates a fast version of:
|
// This generates a fast version of:
|
||||||
// (%_ClassOf(arg) === 'Function')
|
// (%_ClassOf(arg) === 'Function')
|
||||||
|
1
deps/v8/src/arm/codegen-arm.h
vendored
1
deps/v8/src/arm/codegen-arm.h
vendored
@ -475,6 +475,7 @@ class CodeGenerator: public AstVisitor {
|
|||||||
void GenerateIsArray(ZoneList<Expression*>* args);
|
void GenerateIsArray(ZoneList<Expression*>* args);
|
||||||
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
||||||
void GenerateIsObject(ZoneList<Expression*>* args);
|
void GenerateIsObject(ZoneList<Expression*>* args);
|
||||||
|
void GenerateIsSpecObject(ZoneList<Expression*>* args);
|
||||||
void GenerateIsFunction(ZoneList<Expression*>* args);
|
void GenerateIsFunction(ZoneList<Expression*>* args);
|
||||||
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
45
deps/v8/src/arm/disasm-arm.cc
vendored
45
deps/v8/src/arm/disasm-arm.cc
vendored
@ -106,6 +106,7 @@ class Decoder {
|
|||||||
void PrintCondition(Instr* instr);
|
void PrintCondition(Instr* instr);
|
||||||
void PrintShiftRm(Instr* instr);
|
void PrintShiftRm(Instr* instr);
|
||||||
void PrintShiftImm(Instr* instr);
|
void PrintShiftImm(Instr* instr);
|
||||||
|
void PrintShiftSat(Instr* instr);
|
||||||
void PrintPU(Instr* instr);
|
void PrintPU(Instr* instr);
|
||||||
void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
|
void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
|
||||||
|
|
||||||
@ -248,6 +249,18 @@ void Decoder::PrintShiftImm(Instr* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Print the optional shift and immediate used by saturating instructions.
|
||||||
|
void Decoder::PrintShiftSat(Instr* instr) {
|
||||||
|
int shift = instr->Bits(11, 7);
|
||||||
|
if (shift > 0) {
|
||||||
|
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||||
|
", %s #%d",
|
||||||
|
shift_names[instr->Bit(6) * 2],
|
||||||
|
instr->Bits(11, 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Print PU formatting to reduce complexity of FormatOption.
|
// Print PU formatting to reduce complexity of FormatOption.
|
||||||
void Decoder::PrintPU(Instr* instr) {
|
void Decoder::PrintPU(Instr* instr) {
|
||||||
switch (instr->PUField()) {
|
switch (instr->PUField()) {
|
||||||
@ -440,6 +453,20 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
case 'i': { // 'i: immediate value from adjacent bits.
|
||||||
|
// Expects tokens in the form imm%02d@%02d, ie. imm05@07, imm10@16
|
||||||
|
int width = (format[3] - '0') * 10 + (format[4] - '0');
|
||||||
|
int lsb = (format[6] - '0') * 10 + (format[7] - '0');
|
||||||
|
|
||||||
|
ASSERT((width >= 1) && (width <= 32));
|
||||||
|
ASSERT((lsb >= 0) && (lsb <= 31));
|
||||||
|
ASSERT((width + lsb) <= 32);
|
||||||
|
|
||||||
|
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
|
||||||
|
"#%d",
|
||||||
|
instr->Bits(width + lsb - 1, lsb));
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
case 'l': { // 'l: branch and link
|
case 'l': { // 'l: branch and link
|
||||||
if (instr->HasLink()) {
|
if (instr->HasLink()) {
|
||||||
Print("l");
|
Print("l");
|
||||||
@ -507,7 +534,7 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
return FormatRegister(instr, format);
|
return FormatRegister(instr, format);
|
||||||
}
|
}
|
||||||
case 's': {
|
case 's': {
|
||||||
if (format[1] == 'h') { // 'shift_op or 'shift_rm
|
if (format[1] == 'h') { // 'shift_op or 'shift_rm or 'shift_sat.
|
||||||
if (format[6] == 'o') { // 'shift_op
|
if (format[6] == 'o') { // 'shift_op
|
||||||
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
||||||
if (instr->TypeField() == 0) {
|
if (instr->TypeField() == 0) {
|
||||||
@ -517,6 +544,10 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
|
|||||||
PrintShiftImm(instr);
|
PrintShiftImm(instr);
|
||||||
}
|
}
|
||||||
return 8;
|
return 8;
|
||||||
|
} else if (format[6] == 's') { // 'shift_sat.
|
||||||
|
ASSERT(STRING_STARTS_WITH(format, "shift_sat"));
|
||||||
|
PrintShiftSat(instr);
|
||||||
|
return 9;
|
||||||
} else { // 'shift_rm
|
} else { // 'shift_rm
|
||||||
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
||||||
PrintShiftRm(instr);
|
PrintShiftRm(instr);
|
||||||
@ -897,8 +928,16 @@ void Decoder::DecodeType3(Instr* instr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
ASSERT(!instr->HasW());
|
if (instr->HasW()) {
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
ASSERT(instr->Bits(5, 4) == 0x1);
|
||||||
|
if (instr->Bit(22) == 0x1) {
|
||||||
|
Format(instr, "usat 'rd, 'imm05@16, 'rm'shift_sat");
|
||||||
|
} else {
|
||||||
|
UNREACHABLE(); // SSAT.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
19
deps/v8/src/arm/full-codegen-arm.cc
vendored
19
deps/v8/src/arm/full-codegen-arm.cc
vendored
@ -1908,6 +1908,25 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
VisitForValue(args->at(0), kAccumulator);
|
||||||
|
|
||||||
|
Label materialize_true, materialize_false;
|
||||||
|
Label* if_true = NULL;
|
||||||
|
Label* if_false = NULL;
|
||||||
|
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
|
||||||
|
|
||||||
|
__ BranchOnSmi(r0, if_false);
|
||||||
|
__ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
|
||||||
|
__ b(ge, if_true);
|
||||||
|
__ b(if_false);
|
||||||
|
|
||||||
|
Apply(context_, if_true, if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
||||||
ASSERT(args->length() == 1);
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
40
deps/v8/src/arm/ic-arm.cc
vendored
40
deps/v8/src/arm/ic-arm.cc
vendored
@ -958,14 +958,6 @@ static inline bool IsInlinedICSite(Address address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LoadIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Reset the map check of the inlined in-object property load (if present) to
|
|
||||||
// guarantee failure by holding an invalid map (the null value). The offset
|
|
||||||
// can be patched to anything.
|
|
||||||
PatchInlinedLoad(address, Heap::null_value(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
||||||
// Find the end of the inlined code for handling the load if this is an
|
// Find the end of the inlined code for handling the load if this is an
|
||||||
// inlined IC call site.
|
// inlined IC call site.
|
||||||
@ -996,10 +988,9 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void KeyedLoadIC::ClearInlinedVersion(Address address) {
|
bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
|
||||||
// Reset the map check of the inlined keyed load (if present) to
|
// TODO(787): Implement inline stores on arm.
|
||||||
// guarantee failure by holding an invalid map (the null value).
|
return false;
|
||||||
PatchInlinedLoad(address, Heap::null_value());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1018,21 +1009,6 @@ bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Insert null as the elements map to check for. This will make
|
|
||||||
// sure that the elements fast-case map check fails so that control
|
|
||||||
// flows to the IC instead of the inlined version.
|
|
||||||
PatchInlinedStore(address, Heap::null_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::RestoreInlinedVersion(Address address) {
|
|
||||||
// Restore the fast-case elements map check so that the inlined
|
|
||||||
// version can be used again.
|
|
||||||
PatchInlinedStore(address, Heap::fixed_array_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
|
bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
|
||||||
// Find the end of the inlined code for handling the store if this is an
|
// Find the end of the inlined code for handling the store if this is an
|
||||||
// inlined IC call site.
|
// inlined IC call site.
|
||||||
@ -1698,14 +1674,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
|
|||||||
__ cmp(r4, Operand(ip));
|
__ cmp(r4, Operand(ip));
|
||||||
__ b(hs, &slow);
|
__ b(hs, &slow);
|
||||||
__ mov(r5, Operand(value, ASR, kSmiTagSize)); // Untag the value.
|
__ mov(r5, Operand(value, ASR, kSmiTagSize)); // Untag the value.
|
||||||
{ // Clamp the value to [0..255].
|
__ Usat(r5, 8, Operand(r5)); // Clamp the value to [0..255].
|
||||||
Label done;
|
|
||||||
__ tst(r5, Operand(0xFFFFFF00));
|
|
||||||
__ b(eq, &done);
|
|
||||||
__ mov(r5, Operand(0), LeaveCC, mi); // 0 if negative.
|
|
||||||
__ mov(r5, Operand(255), LeaveCC, pl); // 255 if positive.
|
|
||||||
__ bind(&done);
|
|
||||||
}
|
|
||||||
// Get the pointer to the external array. This clobbers elements.
|
// Get the pointer to the external array. This clobbers elements.
|
||||||
__ ldr(elements,
|
__ ldr(elements,
|
||||||
FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
|
FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
|
||||||
|
31
deps/v8/src/arm/macro-assembler-arm.cc
vendored
31
deps/v8/src/arm/macro-assembler-arm.cc
vendored
@ -281,6 +281,37 @@ void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MacroAssembler::Usat(Register dst, int satpos, const Operand& src,
|
||||||
|
Condition cond) {
|
||||||
|
if (!CpuFeatures::IsSupported(ARMv7)) {
|
||||||
|
ASSERT(!dst.is(pc) && !src.rm().is(pc));
|
||||||
|
ASSERT((satpos >= 0) && (satpos <= 31));
|
||||||
|
|
||||||
|
// These asserts are required to ensure compatibility with the ARMv7
|
||||||
|
// implementation.
|
||||||
|
ASSERT((src.shift_op() == ASR) || (src.shift_op() == LSL));
|
||||||
|
ASSERT(src.rs().is(no_reg));
|
||||||
|
|
||||||
|
Label done;
|
||||||
|
int satval = (1 << satpos) - 1;
|
||||||
|
|
||||||
|
if (cond != al) {
|
||||||
|
b(NegateCondition(cond), &done); // Skip saturate if !condition.
|
||||||
|
}
|
||||||
|
if (!(src.is_reg() && dst.is(src.rm()))) {
|
||||||
|
mov(dst, src);
|
||||||
|
}
|
||||||
|
tst(dst, Operand(~satval));
|
||||||
|
b(eq, &done);
|
||||||
|
mov(dst, Operand(0), LeaveCC, mi); // 0 if negative.
|
||||||
|
mov(dst, Operand(satval), LeaveCC, pl); // satval if positive.
|
||||||
|
bind(&done);
|
||||||
|
} else {
|
||||||
|
usat(dst, satpos, src, cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
|
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
|
||||||
// Empty the const pool.
|
// Empty the const pool.
|
||||||
CheckConstPool(true, true);
|
CheckConstPool(true, true);
|
||||||
|
2
deps/v8/src/arm/macro-assembler-arm.h
vendored
2
deps/v8/src/arm/macro-assembler-arm.h
vendored
@ -112,6 +112,8 @@ class MacroAssembler: public Assembler {
|
|||||||
void Sbfx(Register dst, Register src, int lsb, int width,
|
void Sbfx(Register dst, Register src, int lsb, int width,
|
||||||
Condition cond = al);
|
Condition cond = al);
|
||||||
void Bfc(Register dst, int lsb, int width, Condition cond = al);
|
void Bfc(Register dst, int lsb, int width, Condition cond = al);
|
||||||
|
void Usat(Register dst, int satpos, const Operand& src,
|
||||||
|
Condition cond = al);
|
||||||
|
|
||||||
void Call(Label* target);
|
void Call(Label* target);
|
||||||
void Move(Register dst, Handle<Object> value);
|
void Move(Register dst, Handle<Object> value);
|
||||||
|
34
deps/v8/src/arm/simulator-arm.cc
vendored
34
deps/v8/src/arm/simulator-arm.cc
vendored
@ -2047,11 +2047,41 @@ void Simulator::DecodeType3(Instr* instr) {
|
|||||||
case 0: {
|
case 0: {
|
||||||
ASSERT(!instr->HasW());
|
ASSERT(!instr->HasW());
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
||||||
|
UNIMPLEMENTED();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
ASSERT(!instr->HasW());
|
if (instr->HasW()) {
|
||||||
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
ASSERT(instr->Bits(5, 4) == 0x1);
|
||||||
|
|
||||||
|
if (instr->Bit(22) == 0x1) { // USAT.
|
||||||
|
int32_t sat_pos = instr->Bits(20, 16);
|
||||||
|
int32_t sat_val = (1 << sat_pos) - 1;
|
||||||
|
int32_t shift = instr->Bits(11, 7);
|
||||||
|
int32_t shift_type = instr->Bit(6);
|
||||||
|
int32_t rm_val = get_register(instr->RmField());
|
||||||
|
if (shift_type == 0) { // LSL
|
||||||
|
rm_val <<= shift;
|
||||||
|
} else { // ASR
|
||||||
|
rm_val >>= shift;
|
||||||
|
}
|
||||||
|
// If saturation occurs, the Q flag should be set in the CPSR.
|
||||||
|
// There is no Q flag yet, and no instruction (MRS) to read the
|
||||||
|
// CPSR directly.
|
||||||
|
if (rm_val > sat_val) {
|
||||||
|
rm_val = sat_val;
|
||||||
|
} else if (rm_val < 0) {
|
||||||
|
rm_val = 0;
|
||||||
|
}
|
||||||
|
set_register(rd, rm_val);
|
||||||
|
} else { // SSAT.
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
|
3
deps/v8/src/arm/simulator-arm.h
vendored
3
deps/v8/src/arm/simulator-arm.h
vendored
@ -294,6 +294,9 @@ class Simulator {
|
|||||||
void TrashCallerSaveRegisters();
|
void TrashCallerSaveRegisters();
|
||||||
|
|
||||||
// Architecture state.
|
// Architecture state.
|
||||||
|
// Saturating instructions require a Q flag to indicate saturation.
|
||||||
|
// There is currently no way to read the CPSR directly, and thus read the Q
|
||||||
|
// flag, so this is left unimplemented.
|
||||||
int32_t registers_[16];
|
int32_t registers_[16];
|
||||||
bool n_flag_;
|
bool n_flag_;
|
||||||
bool z_flag_;
|
bool z_flag_;
|
||||||
|
2
deps/v8/src/codegen.h
vendored
2
deps/v8/src/codegen.h
vendored
@ -120,6 +120,7 @@ namespace internal {
|
|||||||
F(IsObject, 1, 1) \
|
F(IsObject, 1, 1) \
|
||||||
F(IsFunction, 1, 1) \
|
F(IsFunction, 1, 1) \
|
||||||
F(IsUndetectableObject, 1, 1) \
|
F(IsUndetectableObject, 1, 1) \
|
||||||
|
F(IsSpecObject, 1, 1) \
|
||||||
F(StringAdd, 2, 1) \
|
F(StringAdd, 2, 1) \
|
||||||
F(SubString, 3, 1) \
|
F(SubString, 3, 1) \
|
||||||
F(StringCompare, 2, 1) \
|
F(StringCompare, 2, 1) \
|
||||||
@ -180,7 +181,6 @@ class CodeGeneratorScope BASE_EMBEDDED {
|
|||||||
CodeGenerator* previous_;
|
CodeGenerator* previous_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
|
#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
|
||||||
|
|
||||||
// State of used registers in a virtual frame.
|
// State of used registers in a virtual frame.
|
||||||
|
6
deps/v8/src/compiler.cc
vendored
6
deps/v8/src/compiler.cc
vendored
@ -449,8 +449,12 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
|
|||||||
code);
|
code);
|
||||||
|
|
||||||
// Update the shared function info with the compiled code and the scope info.
|
// Update the shared function info with the compiled code and the scope info.
|
||||||
shared->set_code(*code);
|
// Please note, that the order of the sharedfunction initialization is
|
||||||
|
// important since set_scope_info might trigger a GC, causing the ASSERT
|
||||||
|
// below to be invalid if the code was flushed. By settting the code
|
||||||
|
// object last we avoid this.
|
||||||
shared->set_scope_info(*SerializedScopeInfo::Create(info->scope()));
|
shared->set_scope_info(*SerializedScopeInfo::Create(info->scope()));
|
||||||
|
shared->set_code(*code);
|
||||||
|
|
||||||
// Set the expected number of properties for instances.
|
// Set the expected number of properties for instances.
|
||||||
SetExpectedNofPropertiesFromEstimate(shared, lit->expected_property_count());
|
SetExpectedNofPropertiesFromEstimate(shared, lit->expected_property_count());
|
||||||
|
2
deps/v8/src/full-codegen.cc
vendored
2
deps/v8/src/full-codegen.cc
vendored
@ -857,6 +857,8 @@ void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
|
|||||||
EmitIsNonNegativeSmi(expr->arguments());
|
EmitIsNonNegativeSmi(expr->arguments());
|
||||||
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
|
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
|
||||||
EmitIsObject(expr->arguments());
|
EmitIsObject(expr->arguments());
|
||||||
|
} else if (strcmp("_IsSpecObject", *name->ToCString()) == 0) {
|
||||||
|
EmitIsSpecObject(expr->arguments());
|
||||||
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
|
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
|
||||||
EmitIsUndetectableObject(expr->arguments());
|
EmitIsUndetectableObject(expr->arguments());
|
||||||
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
|
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
|
||||||
|
1
deps/v8/src/full-codegen.h
vendored
1
deps/v8/src/full-codegen.h
vendored
@ -402,6 +402,7 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
void EmitIsSmi(ZoneList<Expression*>* arguments);
|
void EmitIsSmi(ZoneList<Expression*>* arguments);
|
||||||
void EmitIsNonNegativeSmi(ZoneList<Expression*>* arguments);
|
void EmitIsNonNegativeSmi(ZoneList<Expression*>* arguments);
|
||||||
void EmitIsObject(ZoneList<Expression*>* arguments);
|
void EmitIsObject(ZoneList<Expression*>* arguments);
|
||||||
|
void EmitIsSpecObject(ZoneList<Expression*>* arguments);
|
||||||
void EmitIsUndetectableObject(ZoneList<Expression*>* arguments);
|
void EmitIsUndetectableObject(ZoneList<Expression*>* arguments);
|
||||||
void EmitIsFunction(ZoneList<Expression*>* arguments);
|
void EmitIsFunction(ZoneList<Expression*>* arguments);
|
||||||
void EmitIsArray(ZoneList<Expression*>* arguments);
|
void EmitIsArray(ZoneList<Expression*>* arguments);
|
||||||
|
12
deps/v8/src/global-handles.cc
vendored
12
deps/v8/src/global-handles.cc
vendored
@ -151,13 +151,14 @@ class GlobalHandles::Node : public Malloced {
|
|||||||
bool PostGarbageCollectionProcessing() {
|
bool PostGarbageCollectionProcessing() {
|
||||||
if (state_ != Node::PENDING) return false;
|
if (state_ != Node::PENDING) return false;
|
||||||
LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
|
LOG(HandleEvent("GlobalHandle::Processing", handle().location()));
|
||||||
|
WeakReferenceCallback func = callback();
|
||||||
|
if (func == NULL) {
|
||||||
|
Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
void* par = parameter();
|
void* par = parameter();
|
||||||
state_ = NEAR_DEATH;
|
state_ = NEAR_DEATH;
|
||||||
set_parameter(NULL);
|
set_parameter(NULL);
|
||||||
// The callback function is resolved as late as possible to preserve old
|
|
||||||
// behavior.
|
|
||||||
WeakReferenceCallback func = callback();
|
|
||||||
if (func == NULL) return false;
|
|
||||||
|
|
||||||
v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
|
v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
|
||||||
{
|
{
|
||||||
@ -178,6 +179,9 @@ class GlobalHandles::Node : public Malloced {
|
|||||||
VMState state(EXTERNAL);
|
VMState state(EXTERNAL);
|
||||||
func(object, par);
|
func(object, par);
|
||||||
}
|
}
|
||||||
|
// Absense of explicit cleanup or revival of weak handle
|
||||||
|
// in most of the cases would lead to memory leak.
|
||||||
|
ASSERT(state_ != NEAR_DEATH);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
deps/v8/src/handles.cc
vendored
4
deps/v8/src/handles.cc
vendored
@ -664,8 +664,12 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
|
|||||||
// therefore it does not make sense to cache the property names
|
// therefore it does not make sense to cache the property names
|
||||||
// for arguments objects. Arguments objects will always have
|
// for arguments objects. Arguments objects will always have
|
||||||
// elements.
|
// elements.
|
||||||
|
// Wrapped strings have elements, but don't have an elements
|
||||||
|
// array or dictionary. So the fast inline test for whether to
|
||||||
|
// use the cache says yes, so we should not create a cache.
|
||||||
bool cache_enum_keys =
|
bool cache_enum_keys =
|
||||||
((current->map()->constructor() != *arguments_function) &&
|
((current->map()->constructor() != *arguments_function) &&
|
||||||
|
!current->IsJSValue() &&
|
||||||
!current->IsAccessCheckNeeded() &&
|
!current->IsAccessCheckNeeded() &&
|
||||||
!current->HasNamedInterceptor() &&
|
!current->HasNamedInterceptor() &&
|
||||||
!current->HasIndexedInterceptor());
|
!current->HasIndexedInterceptor());
|
||||||
|
7
deps/v8/src/heap-profiler.cc
vendored
7
deps/v8/src/heap-profiler.cc
vendored
@ -364,6 +364,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name) {
|
|||||||
HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
|
HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
|
||||||
HeapSnapshotGenerator generator(result);
|
HeapSnapshotGenerator generator(result);
|
||||||
generator.GenerateSnapshot();
|
generator.GenerateSnapshot();
|
||||||
|
snapshots_->SnapshotGenerationFinished();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,6 +392,12 @@ HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
|
||||||
|
ASSERT(singleton_ != NULL);
|
||||||
|
singleton_->snapshots_->ObjectMoveEvent(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
|
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
|
||||||
const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
|
const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
|
||||||
|
|
||||||
|
16
deps/v8/src/heap-profiler.h
vendored
16
deps/v8/src/heap-profiler.h
vendored
@ -38,7 +38,15 @@ namespace internal {
|
|||||||
class HeapSnapshot;
|
class HeapSnapshot;
|
||||||
class HeapSnapshotsCollection;
|
class HeapSnapshotsCollection;
|
||||||
|
|
||||||
#endif
|
#define HEAP_PROFILE(Call) \
|
||||||
|
do { \
|
||||||
|
if (v8::internal::HeapProfiler::is_profiling()) { \
|
||||||
|
v8::internal::HeapProfiler::Call; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
#else
|
||||||
|
#define HEAP_PROFILE(Call) ((void) 0)
|
||||||
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||||
|
|
||||||
// The HeapProfiler writes data to the log files, which can be postprocessed
|
// The HeapProfiler writes data to the log files, which can be postprocessed
|
||||||
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
|
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
|
||||||
@ -54,6 +62,12 @@ class HeapProfiler {
|
|||||||
static HeapSnapshot* GetSnapshot(int index);
|
static HeapSnapshot* GetSnapshot(int index);
|
||||||
static HeapSnapshot* FindSnapshot(unsigned uid);
|
static HeapSnapshot* FindSnapshot(unsigned uid);
|
||||||
|
|
||||||
|
static void ObjectMoveEvent(Address from, Address to);
|
||||||
|
|
||||||
|
static INLINE(bool is_profiling()) {
|
||||||
|
return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
|
||||||
|
}
|
||||||
|
|
||||||
// Obsolete interface.
|
// Obsolete interface.
|
||||||
// Write a single heap sample to the log file.
|
// Write a single heap sample to the log file.
|
||||||
static void WriteSample();
|
static void WriteSample();
|
||||||
|
3
deps/v8/src/heap.cc
vendored
3
deps/v8/src/heap.cc
vendored
@ -638,6 +638,7 @@ void Heap::PerformGarbageCollection(AllocationSpace space,
|
|||||||
if (collector == MARK_COMPACTOR) {
|
if (collector == MARK_COMPACTOR) {
|
||||||
if (FLAG_flush_code) {
|
if (FLAG_flush_code) {
|
||||||
// Flush all potentially unused code.
|
// Flush all potentially unused code.
|
||||||
|
GCTracer::Scope gc_scope(tracer, GCTracer::Scope::MC_FLUSH_CODE);
|
||||||
FlushCode();
|
FlushCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1107,6 +1108,7 @@ inline static HeapObject* MigrateObject(HeapObject* source,
|
|||||||
// Update NewSpace stats if necessary.
|
// Update NewSpace stats if necessary.
|
||||||
RecordCopiedObject(target);
|
RecordCopiedObject(target);
|
||||||
#endif
|
#endif
|
||||||
|
HEAP_PROFILE(ObjectMoveEvent(source->address(), target->address()));
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@ -4840,6 +4842,7 @@ GCTracer::~GCTracer() {
|
|||||||
PrintF("mark=%d ", static_cast<int>(scopes_[Scope::MC_MARK]));
|
PrintF("mark=%d ", static_cast<int>(scopes_[Scope::MC_MARK]));
|
||||||
PrintF("sweep=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP]));
|
PrintF("sweep=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP]));
|
||||||
PrintF("compact=%d ", static_cast<int>(scopes_[Scope::MC_COMPACT]));
|
PrintF("compact=%d ", static_cast<int>(scopes_[Scope::MC_COMPACT]));
|
||||||
|
PrintF("flushcode=%d ", static_cast<int>(scopes_[Scope::MC_FLUSH_CODE]));
|
||||||
|
|
||||||
PrintF("total_size_before=%d ", start_size_);
|
PrintF("total_size_before=%d ", start_size_);
|
||||||
PrintF("total_size_after=%d ", Heap::SizeOfObjects());
|
PrintF("total_size_after=%d ", Heap::SizeOfObjects());
|
||||||
|
1
deps/v8/src/heap.h
vendored
1
deps/v8/src/heap.h
vendored
@ -1722,6 +1722,7 @@ class GCTracer BASE_EMBEDDED {
|
|||||||
MC_MARK,
|
MC_MARK,
|
||||||
MC_SWEEP,
|
MC_SWEEP,
|
||||||
MC_COMPACT,
|
MC_COMPACT,
|
||||||
|
MC_FLUSH_CODE,
|
||||||
kNumberOfScopes
|
kNumberOfScopes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
474
deps/v8/src/ia32/codegen-ia32.cc
vendored
474
deps/v8/src/ia32/codegen-ia32.cc
vendored
@ -34,12 +34,9 @@
|
|||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "ic-inl.h"
|
#include "ic-inl.h"
|
||||||
#include "jsregexp.h"
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "regexp-macro-assembler.h"
|
#include "regexp-macro-assembler.h"
|
||||||
#include "regexp-stack.h"
|
|
||||||
#include "register-allocator-inl.h"
|
#include "register-allocator-inl.h"
|
||||||
#include "runtime.h"
|
|
||||||
#include "scopes.h"
|
#include "scopes.h"
|
||||||
#include "virtual-frame-inl.h"
|
#include "virtual-frame-inl.h"
|
||||||
|
|
||||||
@ -143,7 +140,7 @@ CodeGenState::~CodeGenState() {
|
|||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// CodeGenerator implementation
|
// CodeGenerator implementation.
|
||||||
|
|
||||||
CodeGenerator::CodeGenerator(MacroAssembler* masm)
|
CodeGenerator::CodeGenerator(MacroAssembler* masm)
|
||||||
: deferred_(8),
|
: deferred_(8),
|
||||||
@ -374,12 +371,11 @@ void CodeGenerator::Generate(CompilationInfo* info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adjust for function-level loop nesting.
|
// Adjust for function-level loop nesting.
|
||||||
ASSERT_EQ(info->loop_nesting(), loop_nesting_);
|
ASSERT_EQ(loop_nesting_, info->loop_nesting());
|
||||||
loop_nesting_ = 0;
|
loop_nesting_ = 0;
|
||||||
|
|
||||||
// Code generation state must be reset.
|
// Code generation state must be reset.
|
||||||
ASSERT(state_ == NULL);
|
ASSERT(state_ == NULL);
|
||||||
ASSERT(loop_nesting() == 0);
|
|
||||||
ASSERT(!function_return_is_shadowed_);
|
ASSERT(!function_return_is_shadowed_);
|
||||||
function_return_.Unuse();
|
function_return_.Unuse();
|
||||||
DeleteFrame();
|
DeleteFrame();
|
||||||
@ -646,7 +642,6 @@ void CodeGenerator::Load(Expression* expr) {
|
|||||||
} else {
|
} else {
|
||||||
JumpTarget true_target;
|
JumpTarget true_target;
|
||||||
JumpTarget false_target;
|
JumpTarget false_target;
|
||||||
|
|
||||||
ControlDestination dest(&true_target, &false_target, true);
|
ControlDestination dest(&true_target, &false_target, true);
|
||||||
LoadCondition(expr, &dest, false);
|
LoadCondition(expr, &dest, false);
|
||||||
|
|
||||||
@ -784,9 +779,9 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
|
|||||||
JumpTarget done;
|
JumpTarget done;
|
||||||
bool skip_arguments = false;
|
bool skip_arguments = false;
|
||||||
if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
|
if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
|
||||||
// We have to skip storing into the arguments slot if it has already
|
// We have to skip storing into the arguments slot if it has
|
||||||
// been written to. This can happen if the a function has a local
|
// already been written to. This can happen if the a function
|
||||||
// variable named 'arguments'.
|
// has a local variable named 'arguments'.
|
||||||
LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF);
|
LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF);
|
||||||
Result probe = frame_->Pop();
|
Result probe = frame_->Pop();
|
||||||
if (probe.is_constant()) {
|
if (probe.is_constant()) {
|
||||||
@ -1434,8 +1429,8 @@ bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
|
|||||||
} else {
|
} else {
|
||||||
unsigned_left >>= shift_amount;
|
unsigned_left >>= shift_amount;
|
||||||
}
|
}
|
||||||
ASSERT(Smi::IsValid(unsigned_left)); // Converted to signed.
|
ASSERT(Smi::IsValid(static_cast<int32_t>(unsigned_left)));
|
||||||
answer_object = Smi::FromInt(unsigned_left); // Converted to signed.
|
answer_object = Smi::FromInt(static_cast<int32_t>(unsigned_left));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1919,12 +1914,12 @@ class DeferredInlineSmiOperationReversed: public DeferredCode {
|
|||||||
|
|
||||||
|
|
||||||
void DeferredInlineSmiOperationReversed::Generate() {
|
void DeferredInlineSmiOperationReversed::Generate() {
|
||||||
GenericBinaryOpStub igostub(
|
GenericBinaryOpStub stub(
|
||||||
op_,
|
op_,
|
||||||
overwrite_mode_,
|
overwrite_mode_,
|
||||||
NO_SMI_CODE_IN_STUB,
|
NO_SMI_CODE_IN_STUB,
|
||||||
TypeInfo::Combine(TypeInfo::Smi(), type_info_));
|
TypeInfo::Combine(TypeInfo::Smi(), type_info_));
|
||||||
igostub.GenerateCall(masm_, value_, src_);
|
stub.GenerateCall(masm_, value_, src_);
|
||||||
if (!dst_.is(eax)) __ mov(dst_, eax);
|
if (!dst_.is(eax)) __ mov(dst_, eax);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2424,6 +2419,7 @@ Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Fall through if we did not find a power of 2 on the right hand side!
|
// Fall through if we did not find a power of 2 on the right hand side!
|
||||||
|
// The next case must be the default.
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Result constant_operand(value);
|
Result constant_operand(value);
|
||||||
@ -2487,8 +2483,7 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
}
|
}
|
||||||
ASSERT(cc == less || cc == equal || cc == greater_equal);
|
ASSERT(cc == less || cc == equal || cc == greater_equal);
|
||||||
|
|
||||||
// If either side is a constant of some sort, we can probably optimize the
|
// If either side is a constant smi, optimize the comparison.
|
||||||
// comparison.
|
|
||||||
bool left_side_constant_smi = false;
|
bool left_side_constant_smi = false;
|
||||||
bool left_side_constant_null = false;
|
bool left_side_constant_null = false;
|
||||||
bool left_side_constant_1_char_string = false;
|
bool left_side_constant_1_char_string = false;
|
||||||
@ -2513,114 +2508,11 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (left_side_constant_smi || right_side_constant_smi) {
|
if (left_side_constant_smi || right_side_constant_smi) {
|
||||||
if (left_side_constant_smi && right_side_constant_smi) {
|
bool is_loop_condition = (node->AsExpression() != NULL) &&
|
||||||
// Trivial case, comparing two constants.
|
node->AsExpression()->is_loop_condition();
|
||||||
int left_value = Smi::cast(*left_side.handle())->value();
|
ConstantSmiComparison(cc, strict, dest, &left_side, &right_side,
|
||||||
int right_value = Smi::cast(*right_side.handle())->value();
|
left_side_constant_smi, right_side_constant_smi,
|
||||||
switch (cc) {
|
is_loop_condition);
|
||||||
case less:
|
|
||||||
dest->Goto(left_value < right_value);
|
|
||||||
break;
|
|
||||||
case equal:
|
|
||||||
dest->Goto(left_value == right_value);
|
|
||||||
break;
|
|
||||||
case greater_equal:
|
|
||||||
dest->Goto(left_value >= right_value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Only one side is a constant Smi.
|
|
||||||
// If left side is a constant Smi, reverse the operands.
|
|
||||||
// Since one side is a constant Smi, conversion order does not matter.
|
|
||||||
if (left_side_constant_smi) {
|
|
||||||
Result temp = left_side;
|
|
||||||
left_side = right_side;
|
|
||||||
right_side = temp;
|
|
||||||
cc = ReverseCondition(cc);
|
|
||||||
// This may re-introduce greater or less_equal as the value of cc.
|
|
||||||
// CompareStub and the inline code both support all values of cc.
|
|
||||||
}
|
|
||||||
// Implement comparison against a constant Smi, inlining the case
|
|
||||||
// where both sides are Smis.
|
|
||||||
left_side.ToRegister();
|
|
||||||
Register left_reg = left_side.reg();
|
|
||||||
Handle<Object> right_val = right_side.handle();
|
|
||||||
|
|
||||||
// Here we split control flow to the stub call and inlined cases
|
|
||||||
// before finally splitting it to the control destination. We use
|
|
||||||
// a jump target and branching to duplicate the virtual frame at
|
|
||||||
// the first split. We manually handle the off-frame references
|
|
||||||
// by reconstituting them on the non-fall-through path.
|
|
||||||
|
|
||||||
if (left_side.is_smi()) {
|
|
||||||
if (FLAG_debug_code) {
|
|
||||||
__ AbortIfNotSmi(left_side.reg());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
JumpTarget is_smi;
|
|
||||||
__ test(left_side.reg(), Immediate(kSmiTagMask));
|
|
||||||
is_smi.Branch(zero, taken);
|
|
||||||
|
|
||||||
bool is_loop_condition = (node->AsExpression() != NULL) &&
|
|
||||||
node->AsExpression()->is_loop_condition();
|
|
||||||
if (!is_loop_condition &&
|
|
||||||
CpuFeatures::IsSupported(SSE2) &&
|
|
||||||
right_val->IsSmi()) {
|
|
||||||
// Right side is a constant smi and left side has been checked
|
|
||||||
// not to be a smi.
|
|
||||||
CpuFeatures::Scope use_sse2(SSE2);
|
|
||||||
JumpTarget not_number;
|
|
||||||
__ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
|
|
||||||
Immediate(Factory::heap_number_map()));
|
|
||||||
not_number.Branch(not_equal, &left_side);
|
|
||||||
__ movdbl(xmm1,
|
|
||||||
FieldOperand(left_reg, HeapNumber::kValueOffset));
|
|
||||||
int value = Smi::cast(*right_val)->value();
|
|
||||||
if (value == 0) {
|
|
||||||
__ xorpd(xmm0, xmm0);
|
|
||||||
} else {
|
|
||||||
Result temp = allocator()->Allocate();
|
|
||||||
__ mov(temp.reg(), Immediate(value));
|
|
||||||
__ cvtsi2sd(xmm0, Operand(temp.reg()));
|
|
||||||
temp.Unuse();
|
|
||||||
}
|
|
||||||
__ ucomisd(xmm1, xmm0);
|
|
||||||
// Jump to builtin for NaN.
|
|
||||||
not_number.Branch(parity_even, &left_side);
|
|
||||||
left_side.Unuse();
|
|
||||||
dest->true_target()->Branch(DoubleCondition(cc));
|
|
||||||
dest->false_target()->Jump();
|
|
||||||
not_number.Bind(&left_side);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup and call the compare stub.
|
|
||||||
CompareStub stub(cc, strict, kCantBothBeNaN);
|
|
||||||
Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
|
||||||
result.ToRegister();
|
|
||||||
__ cmp(result.reg(), 0);
|
|
||||||
result.Unuse();
|
|
||||||
dest->true_target()->Branch(cc);
|
|
||||||
dest->false_target()->Jump();
|
|
||||||
|
|
||||||
is_smi.Bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
left_side = Result(left_reg);
|
|
||||||
right_side = Result(right_val);
|
|
||||||
// Test smi equality and comparison by signed int comparison.
|
|
||||||
if (IsUnsafeSmi(right_side.handle())) {
|
|
||||||
right_side.ToRegister();
|
|
||||||
__ cmp(left_side.reg(), Operand(right_side.reg()));
|
|
||||||
} else {
|
|
||||||
__ cmp(Operand(left_side.reg()), Immediate(right_side.handle()));
|
|
||||||
}
|
|
||||||
left_side.Unuse();
|
|
||||||
right_side.Unuse();
|
|
||||||
dest->Split(cc);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (cc == equal &&
|
} else if (cc == equal &&
|
||||||
(left_side_constant_null || right_side_constant_null)) {
|
(left_side_constant_null || right_side_constant_null)) {
|
||||||
// To make null checks efficient, we check if either the left side or
|
// To make null checks efficient, we check if either the left side or
|
||||||
@ -2780,13 +2672,14 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Neither side is a constant Smi, constant 1-char string or constant null.
|
// Neither side is a constant Smi, constant 1-char string or constant null.
|
||||||
// If either side is a non-smi constant, or known to be a heap number skip
|
// If either side is a non-smi constant, or known to be a heap number,
|
||||||
// the smi check.
|
// skip the smi check.
|
||||||
bool known_non_smi =
|
bool known_non_smi =
|
||||||
(left_side.is_constant() && !left_side.handle()->IsSmi()) ||
|
(left_side.is_constant() && !left_side.handle()->IsSmi()) ||
|
||||||
(right_side.is_constant() && !right_side.handle()->IsSmi()) ||
|
(right_side.is_constant() && !right_side.handle()->IsSmi()) ||
|
||||||
left_side.type_info().IsDouble() ||
|
left_side.type_info().IsDouble() ||
|
||||||
right_side.type_info().IsDouble();
|
right_side.type_info().IsDouble();
|
||||||
|
|
||||||
NaNInformation nan_info =
|
NaNInformation nan_info =
|
||||||
(CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
|
(CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
|
||||||
kBothCouldBeNaN :
|
kBothCouldBeNaN :
|
||||||
@ -2811,14 +2704,15 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
right_side.ToRegister();
|
right_side.ToRegister();
|
||||||
|
|
||||||
if (known_non_smi) {
|
if (known_non_smi) {
|
||||||
// Inline the equality check if both operands can't be a NaN. If both
|
// Inlined equality check:
|
||||||
// objects are the same they are equal.
|
// If at least one of the objects is not NaN, then if the objects
|
||||||
|
// are identical, they are equal.
|
||||||
if (nan_info == kCantBothBeNaN && cc == equal) {
|
if (nan_info == kCantBothBeNaN && cc == equal) {
|
||||||
__ cmp(left_side.reg(), Operand(right_side.reg()));
|
__ cmp(left_side.reg(), Operand(right_side.reg()));
|
||||||
dest->true_target()->Branch(equal);
|
dest->true_target()->Branch(equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline number comparison.
|
// Inlined number comparison:
|
||||||
if (inline_number_compare) {
|
if (inline_number_compare) {
|
||||||
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
||||||
}
|
}
|
||||||
@ -2856,7 +2750,7 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
dest->true_target()->Branch(equal);
|
dest->true_target()->Branch(equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline number comparison.
|
// Inlined number comparison:
|
||||||
if (inline_number_compare) {
|
if (inline_number_compare) {
|
||||||
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
||||||
}
|
}
|
||||||
@ -2882,6 +2776,139 @@ void CodeGenerator::Comparison(AstNode* node,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::ConstantSmiComparison(Condition cc,
|
||||||
|
bool strict,
|
||||||
|
ControlDestination* dest,
|
||||||
|
Result* left_side,
|
||||||
|
Result* right_side,
|
||||||
|
bool left_side_constant_smi,
|
||||||
|
bool right_side_constant_smi,
|
||||||
|
bool is_loop_condition) {
|
||||||
|
if (left_side_constant_smi && right_side_constant_smi) {
|
||||||
|
// Trivial case, comparing two constants.
|
||||||
|
int left_value = Smi::cast(*left_side->handle())->value();
|
||||||
|
int right_value = Smi::cast(*right_side->handle())->value();
|
||||||
|
switch (cc) {
|
||||||
|
case less:
|
||||||
|
dest->Goto(left_value < right_value);
|
||||||
|
break;
|
||||||
|
case equal:
|
||||||
|
dest->Goto(left_value == right_value);
|
||||||
|
break;
|
||||||
|
case greater_equal:
|
||||||
|
dest->Goto(left_value >= right_value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only one side is a constant Smi.
|
||||||
|
// If left side is a constant Smi, reverse the operands.
|
||||||
|
// Since one side is a constant Smi, conversion order does not matter.
|
||||||
|
if (left_side_constant_smi) {
|
||||||
|
Result* temp = left_side;
|
||||||
|
left_side = right_side;
|
||||||
|
right_side = temp;
|
||||||
|
cc = ReverseCondition(cc);
|
||||||
|
// This may re-introduce greater or less_equal as the value of cc.
|
||||||
|
// CompareStub and the inline code both support all values of cc.
|
||||||
|
}
|
||||||
|
// Implement comparison against a constant Smi, inlining the case
|
||||||
|
// where both sides are Smis.
|
||||||
|
left_side->ToRegister();
|
||||||
|
Register left_reg = left_side->reg();
|
||||||
|
Handle<Object> right_val = right_side->handle();
|
||||||
|
|
||||||
|
if (left_side->is_smi()) {
|
||||||
|
if (FLAG_debug_code) {
|
||||||
|
__ AbortIfNotSmi(left_reg);
|
||||||
|
}
|
||||||
|
// Test smi equality and comparison by signed int comparison.
|
||||||
|
if (IsUnsafeSmi(right_side->handle())) {
|
||||||
|
right_side->ToRegister();
|
||||||
|
__ cmp(left_reg, Operand(right_side->reg()));
|
||||||
|
} else {
|
||||||
|
__ cmp(Operand(left_reg), Immediate(right_side->handle()));
|
||||||
|
}
|
||||||
|
left_side->Unuse();
|
||||||
|
right_side->Unuse();
|
||||||
|
dest->Split(cc);
|
||||||
|
} else {
|
||||||
|
// Only the case where the left side could possibly be a non-smi is left.
|
||||||
|
JumpTarget is_smi;
|
||||||
|
if (cc == equal) {
|
||||||
|
// We can do the equality comparison before the smi check.
|
||||||
|
__ cmp(Operand(left_reg), Immediate(right_side->handle()));
|
||||||
|
dest->true_target()->Branch(equal);
|
||||||
|
__ test(left_reg, Immediate(kSmiTagMask));
|
||||||
|
dest->false_target()->Branch(zero);
|
||||||
|
} else {
|
||||||
|
// Do the smi check, then the comparison.
|
||||||
|
JumpTarget is_not_smi;
|
||||||
|
__ test(left_reg, Immediate(kSmiTagMask));
|
||||||
|
is_smi.Branch(zero, left_side, right_side);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump or fall through to here if we are comparing a non-smi to a
|
||||||
|
// constant smi. If the non-smi is a heap number and this is not
|
||||||
|
// a loop condition, inline the floating point code.
|
||||||
|
if (!is_loop_condition && CpuFeatures::IsSupported(SSE2)) {
|
||||||
|
// Right side is a constant smi and left side has been checked
|
||||||
|
// not to be a smi.
|
||||||
|
CpuFeatures::Scope use_sse2(SSE2);
|
||||||
|
JumpTarget not_number;
|
||||||
|
__ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
|
||||||
|
Immediate(Factory::heap_number_map()));
|
||||||
|
not_number.Branch(not_equal, left_side);
|
||||||
|
__ movdbl(xmm1,
|
||||||
|
FieldOperand(left_reg, HeapNumber::kValueOffset));
|
||||||
|
int value = Smi::cast(*right_val)->value();
|
||||||
|
if (value == 0) {
|
||||||
|
__ xorpd(xmm0, xmm0);
|
||||||
|
} else {
|
||||||
|
Result temp = allocator()->Allocate();
|
||||||
|
__ mov(temp.reg(), Immediate(value));
|
||||||
|
__ cvtsi2sd(xmm0, Operand(temp.reg()));
|
||||||
|
temp.Unuse();
|
||||||
|
}
|
||||||
|
__ ucomisd(xmm1, xmm0);
|
||||||
|
// Jump to builtin for NaN.
|
||||||
|
not_number.Branch(parity_even, left_side);
|
||||||
|
left_side->Unuse();
|
||||||
|
dest->true_target()->Branch(DoubleCondition(cc));
|
||||||
|
dest->false_target()->Jump();
|
||||||
|
not_number.Bind(left_side);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup and call the compare stub.
|
||||||
|
CompareStub stub(cc, strict, kCantBothBeNaN);
|
||||||
|
Result result = frame_->CallStub(&stub, left_side, right_side);
|
||||||
|
result.ToRegister();
|
||||||
|
__ test(result.reg(), Operand(result.reg()));
|
||||||
|
result.Unuse();
|
||||||
|
if (cc == equal) {
|
||||||
|
dest->Split(cc);
|
||||||
|
} else {
|
||||||
|
dest->true_target()->Branch(cc);
|
||||||
|
dest->false_target()->Jump();
|
||||||
|
|
||||||
|
// It is important for performance for this case to be at the end.
|
||||||
|
is_smi.Bind(left_side, right_side);
|
||||||
|
if (IsUnsafeSmi(right_side->handle())) {
|
||||||
|
right_side->ToRegister();
|
||||||
|
__ cmp(left_reg, Operand(right_side->reg()));
|
||||||
|
} else {
|
||||||
|
__ cmp(Operand(left_reg), Immediate(right_side->handle()));
|
||||||
|
}
|
||||||
|
left_side->Unuse();
|
||||||
|
right_side->Unuse();
|
||||||
|
dest->Split(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check that the comparison operand is a number. Jump to not_numbers jump
|
// Check that the comparison operand is a number. Jump to not_numbers jump
|
||||||
// target passing the left and right result if the operand is not a number.
|
// target passing the left and right result if the operand is not a number.
|
||||||
static void CheckComparisonOperand(MacroAssembler* masm_,
|
static void CheckComparisonOperand(MacroAssembler* masm_,
|
||||||
@ -2941,19 +2968,19 @@ static void LoadComparisonOperand(MacroAssembler* masm_,
|
|||||||
// target passing the left and right result if the operand is not a number.
|
// target passing the left and right result if the operand is not a number.
|
||||||
static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
|
static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
|
||||||
Result* operand,
|
Result* operand,
|
||||||
XMMRegister reg,
|
XMMRegister xmm_reg,
|
||||||
Result* left_side,
|
Result* left_side,
|
||||||
Result* right_side,
|
Result* right_side,
|
||||||
JumpTarget* not_numbers) {
|
JumpTarget* not_numbers) {
|
||||||
Label done;
|
Label done;
|
||||||
if (operand->type_info().IsDouble()) {
|
if (operand->type_info().IsDouble()) {
|
||||||
// Operand is known to be a heap number, just load it.
|
// Operand is known to be a heap number, just load it.
|
||||||
__ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
|
__ movdbl(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
|
||||||
} else if (operand->type_info().IsSmi()) {
|
} else if (operand->type_info().IsSmi()) {
|
||||||
// Operand is known to be a smi. Convert it to double and keep the original
|
// Operand is known to be a smi. Convert it to double and keep the original
|
||||||
// smi.
|
// smi.
|
||||||
__ SmiUntag(operand->reg());
|
__ SmiUntag(operand->reg());
|
||||||
__ cvtsi2sd(reg, Operand(operand->reg()));
|
__ cvtsi2sd(xmm_reg, Operand(operand->reg()));
|
||||||
__ SmiTag(operand->reg());
|
__ SmiTag(operand->reg());
|
||||||
} else {
|
} else {
|
||||||
// Operand type not known, check for smi or heap number.
|
// Operand type not known, check for smi or heap number.
|
||||||
@ -2965,13 +2992,13 @@ static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
|
|||||||
Immediate(Factory::heap_number_map()));
|
Immediate(Factory::heap_number_map()));
|
||||||
not_numbers->Branch(not_equal, left_side, right_side, taken);
|
not_numbers->Branch(not_equal, left_side, right_side, taken);
|
||||||
}
|
}
|
||||||
__ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
|
__ movdbl(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
|
||||||
__ jmp(&done);
|
__ jmp(&done);
|
||||||
|
|
||||||
__ bind(&smi);
|
__ bind(&smi);
|
||||||
// Comvert smi to float and keep the original smi.
|
// Comvert smi to float and keep the original smi.
|
||||||
__ SmiUntag(operand->reg());
|
__ SmiUntag(operand->reg());
|
||||||
__ cvtsi2sd(reg, Operand(operand->reg()));
|
__ cvtsi2sd(xmm_reg, Operand(operand->reg()));
|
||||||
__ SmiTag(operand->reg());
|
__ SmiTag(operand->reg());
|
||||||
__ jmp(&done);
|
__ jmp(&done);
|
||||||
}
|
}
|
||||||
@ -3568,8 +3595,10 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) {
|
|||||||
return_value->ToRegister(eax);
|
return_value->ToRegister(eax);
|
||||||
|
|
||||||
// Add a label for checking the size of the code used for returning.
|
// Add a label for checking the size of the code used for returning.
|
||||||
|
#ifdef DEBUG
|
||||||
Label check_exit_codesize;
|
Label check_exit_codesize;
|
||||||
masm_->bind(&check_exit_codesize);
|
masm_->bind(&check_exit_codesize);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Leave the frame and return popping the arguments and the
|
// Leave the frame and return popping the arguments and the
|
||||||
// receiver.
|
// receiver.
|
||||||
@ -3690,7 +3719,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The last instruction emitted was a jump, either to the default
|
// The last instruction emitted was a jump, either to the default
|
||||||
// clause or the break target, or else to a case body from the loop
|
// clause or the break target, or else to a case body from the loop
|
||||||
// that compiles the tests.
|
// that compiles the tests.
|
||||||
@ -3778,8 +3806,8 @@ void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
|
|||||||
// Compile the test.
|
// Compile the test.
|
||||||
switch (info) {
|
switch (info) {
|
||||||
case ALWAYS_TRUE:
|
case ALWAYS_TRUE:
|
||||||
// If control flow can fall off the end of the body, jump back to
|
// If control flow can fall off the end of the body, jump back
|
||||||
// the top and bind the break target at the exit.
|
// to the top and bind the break target at the exit.
|
||||||
if (has_valid_frame()) {
|
if (has_valid_frame()) {
|
||||||
node->continue_target()->Jump();
|
node->continue_target()->Jump();
|
||||||
}
|
}
|
||||||
@ -3815,6 +3843,8 @@ void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DecrementLoopNesting();
|
DecrementLoopNesting();
|
||||||
|
node->continue_target()->Unuse();
|
||||||
|
node->break_target()->Unuse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3899,8 +3929,8 @@ void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
|
|||||||
break;
|
break;
|
||||||
case DONT_KNOW:
|
case DONT_KNOW:
|
||||||
if (test_at_bottom) {
|
if (test_at_bottom) {
|
||||||
// If we have chosen to recompile the test at the bottom, then
|
// If we have chosen to recompile the test at the bottom,
|
||||||
// it is the continue target.
|
// then it is the continue target.
|
||||||
if (node->continue_target()->is_linked()) {
|
if (node->continue_target()->is_linked()) {
|
||||||
node->continue_target()->Bind();
|
node->continue_target()->Bind();
|
||||||
}
|
}
|
||||||
@ -4016,6 +4046,7 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
|
|||||||
node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
||||||
loop.Bind();
|
loop.Bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the test with the body as the true target and preferred
|
// Compile the test with the body as the true target and preferred
|
||||||
// fall-through and with the break target as the false target.
|
// fall-through and with the break target as the false target.
|
||||||
ControlDestination dest(&body, node->break_target(), true);
|
ControlDestination dest(&body, node->break_target(), true);
|
||||||
@ -4125,8 +4156,8 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The break target may be already bound (by the condition), or
|
// The break target may be already bound (by the condition), or there
|
||||||
// there may not be a valid frame. Bind it only if needed.
|
// may not be a valid frame. Bind it only if needed.
|
||||||
if (node->break_target()->is_linked()) {
|
if (node->break_target()->is_linked()) {
|
||||||
node->break_target()->Bind();
|
node->break_target()->Bind();
|
||||||
}
|
}
|
||||||
@ -5309,6 +5340,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
|
|||||||
frame_->Dup();
|
frame_->Dup();
|
||||||
Load(property->value());
|
Load(property->value());
|
||||||
Result dummy = frame_->CallStoreIC(Handle<String>::cast(key), false);
|
Result dummy = frame_->CallStoreIC(Handle<String>::cast(key), false);
|
||||||
|
// A test eax instruction following the store IC call would
|
||||||
|
// indicate the presence of an inlined version of the
|
||||||
|
// store. Add a nop to indicate that there is no such
|
||||||
|
// inlined version.
|
||||||
|
__ nop();
|
||||||
dummy.Unuse();
|
dummy.Unuse();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -6406,6 +6442,27 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
// This generates a fast version of:
|
||||||
|
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
|
||||||
|
// typeof(arg) == function).
|
||||||
|
// It includes undetectable objects (as opposed to IsObject).
|
||||||
|
ASSERT(args->length() == 1);
|
||||||
|
Load(args->at(0));
|
||||||
|
Result value = frame_->Pop();
|
||||||
|
value.ToRegister();
|
||||||
|
ASSERT(value.is_valid());
|
||||||
|
__ test(value.reg(), Immediate(kSmiTagMask));
|
||||||
|
destination()->false_target()->Branch(equal);
|
||||||
|
|
||||||
|
// Check that this is an object.
|
||||||
|
frame_->Spill(value.reg());
|
||||||
|
__ CmpObjectType(value.reg(), FIRST_JS_OBJECT_TYPE, value.reg());
|
||||||
|
value.Unuse();
|
||||||
|
destination()->Split(above_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
||||||
// This generates a fast version of:
|
// This generates a fast version of:
|
||||||
// (%_ClassOf(arg) === 'Function')
|
// (%_ClassOf(arg) === 'Function')
|
||||||
@ -8809,7 +8866,97 @@ Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int expected_height = frame()->height() - (is_contextual ? 1 : 2);
|
int expected_height = frame()->height() - (is_contextual ? 1 : 2);
|
||||||
#endif
|
#endif
|
||||||
Result result = frame()->CallStoreIC(name, is_contextual);
|
|
||||||
|
Result result;
|
||||||
|
if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
|
||||||
|
result = frame()->CallStoreIC(name, is_contextual);
|
||||||
|
// A test eax instruction following the call signals that the inobject
|
||||||
|
// property case was inlined. Ensure that there is not a test eax
|
||||||
|
// instruction here.
|
||||||
|
__ nop();
|
||||||
|
} else {
|
||||||
|
// Inline the in-object property case.
|
||||||
|
JumpTarget slow, done;
|
||||||
|
Label patch_site;
|
||||||
|
|
||||||
|
// Get the value and receiver from the stack.
|
||||||
|
Result value = frame()->Pop();
|
||||||
|
value.ToRegister();
|
||||||
|
Result receiver = frame()->Pop();
|
||||||
|
receiver.ToRegister();
|
||||||
|
|
||||||
|
// Allocate result register.
|
||||||
|
result = allocator()->Allocate();
|
||||||
|
ASSERT(result.is_valid() && receiver.is_valid() && value.is_valid());
|
||||||
|
|
||||||
|
// Check that the receiver is a heap object.
|
||||||
|
__ test(receiver.reg(), Immediate(kSmiTagMask));
|
||||||
|
slow.Branch(zero, &value, &receiver);
|
||||||
|
|
||||||
|
// This is the map check instruction that will be patched (so we can't
|
||||||
|
// use the double underscore macro that may insert instructions).
|
||||||
|
// Initially use an invalid map to force a failure.
|
||||||
|
__ bind(&patch_site);
|
||||||
|
masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
|
||||||
|
Immediate(Factory::null_value()));
|
||||||
|
// This branch is always a forwards branch so it's always a fixed size
|
||||||
|
// which allows the assert below to succeed and patching to work.
|
||||||
|
slow.Branch(not_equal, &value, &receiver);
|
||||||
|
|
||||||
|
// The delta from the patch label to the store offset must be
|
||||||
|
// statically known.
|
||||||
|
ASSERT(masm()->SizeOfCodeGeneratedSince(&patch_site) ==
|
||||||
|
StoreIC::kOffsetToStoreInstruction);
|
||||||
|
|
||||||
|
// The initial (invalid) offset has to be large enough to force a 32-bit
|
||||||
|
// instruction encoding to allow patching with an arbitrary offset. Use
|
||||||
|
// kMaxInt (minus kHeapObjectTag).
|
||||||
|
int offset = kMaxInt;
|
||||||
|
__ mov(FieldOperand(receiver.reg(), offset), value.reg());
|
||||||
|
__ mov(result.reg(), Operand(value.reg()));
|
||||||
|
|
||||||
|
// Allocate scratch register for write barrier.
|
||||||
|
Result scratch = allocator()->Allocate();
|
||||||
|
ASSERT(scratch.is_valid() &&
|
||||||
|
result.is_valid() &&
|
||||||
|
receiver.is_valid() &&
|
||||||
|
value.is_valid());
|
||||||
|
|
||||||
|
// The write barrier clobbers all input registers, so spill the
|
||||||
|
// receiver and the value.
|
||||||
|
frame_->Spill(receiver.reg());
|
||||||
|
frame_->Spill(value.reg());
|
||||||
|
|
||||||
|
// Update the write barrier. To save instructions in the inlined
|
||||||
|
// version we do not filter smis.
|
||||||
|
Label skip_write_barrier;
|
||||||
|
__ InNewSpace(receiver.reg(), value.reg(), equal, &skip_write_barrier);
|
||||||
|
int delta_to_record_write = masm_->SizeOfCodeGeneratedSince(&patch_site);
|
||||||
|
__ lea(scratch.reg(), Operand(receiver.reg(), offset));
|
||||||
|
__ RecordWriteHelper(receiver.reg(), scratch.reg(), value.reg());
|
||||||
|
if (FLAG_debug_code) {
|
||||||
|
__ mov(receiver.reg(), Immediate(BitCast<int32_t>(kZapValue)));
|
||||||
|
__ mov(value.reg(), Immediate(BitCast<int32_t>(kZapValue)));
|
||||||
|
__ mov(scratch.reg(), Immediate(BitCast<int32_t>(kZapValue)));
|
||||||
|
}
|
||||||
|
__ bind(&skip_write_barrier);
|
||||||
|
value.Unuse();
|
||||||
|
scratch.Unuse();
|
||||||
|
receiver.Unuse();
|
||||||
|
done.Jump(&result);
|
||||||
|
|
||||||
|
slow.Bind(&value, &receiver);
|
||||||
|
frame()->Push(&receiver);
|
||||||
|
frame()->Push(&value);
|
||||||
|
result = frame()->CallStoreIC(name, is_contextual);
|
||||||
|
// Encode the offset to the map check instruction and the offset
|
||||||
|
// to the write barrier store address computation in a test eax
|
||||||
|
// instruction.
|
||||||
|
int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site);
|
||||||
|
__ test(eax,
|
||||||
|
Immediate((delta_to_record_write << 16) | delta_to_patch_site));
|
||||||
|
done.Bind(&result);
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_EQ(expected_height, frame()->height());
|
ASSERT_EQ(expected_height, frame()->height());
|
||||||
return result;
|
return result;
|
||||||
@ -11787,12 +11934,6 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
|||||||
__ bind(&slow);
|
__ bind(&slow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push arguments below the return address.
|
|
||||||
__ pop(ecx);
|
|
||||||
__ push(eax);
|
|
||||||
__ push(edx);
|
|
||||||
__ push(ecx);
|
|
||||||
|
|
||||||
// Generate the number comparison code.
|
// Generate the number comparison code.
|
||||||
if (include_number_compare_) {
|
if (include_number_compare_) {
|
||||||
Label non_number_comparison;
|
Label non_number_comparison;
|
||||||
@ -11812,33 +11953,32 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
|||||||
__ cmov(above, eax, Operand(ecx));
|
__ cmov(above, eax, Operand(ecx));
|
||||||
__ mov(ecx, Immediate(Smi::FromInt(-1)));
|
__ mov(ecx, Immediate(Smi::FromInt(-1)));
|
||||||
__ cmov(below, eax, Operand(ecx));
|
__ cmov(below, eax, Operand(ecx));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
} else {
|
} else {
|
||||||
FloatingPointHelper::CheckFloatOperands(
|
FloatingPointHelper::CheckFloatOperands(
|
||||||
masm, &non_number_comparison, ebx);
|
masm, &non_number_comparison, ebx);
|
||||||
FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
FloatingPointHelper::LoadFloatOperand(masm, eax);
|
||||||
|
FloatingPointHelper::LoadFloatOperand(masm, edx);
|
||||||
__ FCmp();
|
__ FCmp();
|
||||||
|
|
||||||
// Don't base result on EFLAGS when a NaN is involved.
|
// Don't base result on EFLAGS when a NaN is involved.
|
||||||
__ j(parity_even, &unordered, not_taken);
|
__ j(parity_even, &unordered, not_taken);
|
||||||
|
|
||||||
Label below_label, above_label;
|
Label below_label, above_label;
|
||||||
// Return a result of -1, 0, or 1, based on EFLAGS. In all cases remove
|
// Return a result of -1, 0, or 1, based on EFLAGS.
|
||||||
// two arguments from the stack as they have been pushed in preparation
|
|
||||||
// of a possible runtime call.
|
|
||||||
__ j(below, &below_label, not_taken);
|
__ j(below, &below_label, not_taken);
|
||||||
__ j(above, &above_label, not_taken);
|
__ j(above, &above_label, not_taken);
|
||||||
|
|
||||||
__ xor_(eax, Operand(eax));
|
__ xor_(eax, Operand(eax));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&below_label);
|
__ bind(&below_label);
|
||||||
__ mov(eax, Immediate(Smi::FromInt(-1)));
|
__ mov(eax, Immediate(Smi::FromInt(-1)));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&above_label);
|
__ bind(&above_label);
|
||||||
__ mov(eax, Immediate(Smi::FromInt(1)));
|
__ mov(eax, Immediate(Smi::FromInt(1)));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the numbers was NaN, then the result is always false.
|
// If one of the numbers was NaN, then the result is always false.
|
||||||
@ -11850,7 +11990,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
|||||||
} else {
|
} else {
|
||||||
__ mov(eax, Immediate(Smi::FromInt(-1)));
|
__ mov(eax, Immediate(Smi::FromInt(-1)));
|
||||||
}
|
}
|
||||||
__ ret(2 * kPointerSize); // eax, edx were pushed
|
__ ret(0);
|
||||||
|
|
||||||
// The number comparison code did not provide a valid result.
|
// The number comparison code did not provide a valid result.
|
||||||
__ bind(&non_number_comparison);
|
__ bind(&non_number_comparison);
|
||||||
@ -11865,7 +12005,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
|||||||
// We've already checked for object identity, so if both operands
|
// We've already checked for object identity, so if both operands
|
||||||
// are symbols they aren't equal. Register eax already holds a
|
// are symbols they aren't equal. Register eax already holds a
|
||||||
// non-zero value, which indicates not equal, so just return.
|
// non-zero value, which indicates not equal, so just return.
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
__ bind(&check_for_strings);
|
__ bind(&check_for_strings);
|
||||||
@ -11918,14 +12058,12 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
|||||||
__ bind(&return_unequal);
|
__ bind(&return_unequal);
|
||||||
// Return non-equal by returning the non-zero object pointer in eax,
|
// Return non-equal by returning the non-zero object pointer in eax,
|
||||||
// or return equal if we fell through to here.
|
// or return equal if we fell through to here.
|
||||||
__ ret(2 * kPointerSize); // rax, rdx were pushed
|
__ ret(0); // rax, rdx were pushed
|
||||||
__ bind(¬_both_objects);
|
__ bind(¬_both_objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
// must swap argument order
|
// Push arguments below the return address.
|
||||||
__ pop(ecx);
|
__ pop(ecx);
|
||||||
__ pop(edx);
|
|
||||||
__ pop(eax);
|
|
||||||
__ push(edx);
|
__ push(edx);
|
||||||
__ push(eax);
|
__ push(eax);
|
||||||
|
|
||||||
@ -13502,19 +13640,19 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
|||||||
ASSERT_EQ(0, EQUAL);
|
ASSERT_EQ(0, EQUAL);
|
||||||
ASSERT_EQ(0, kSmiTag);
|
ASSERT_EQ(0, kSmiTag);
|
||||||
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
|
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&result_not_equal);
|
__ bind(&result_not_equal);
|
||||||
__ j(greater, &result_greater);
|
__ j(greater, &result_greater);
|
||||||
|
|
||||||
// Result is LESS.
|
// Result is LESS.
|
||||||
__ Set(eax, Immediate(Smi::FromInt(LESS)));
|
__ Set(eax, Immediate(Smi::FromInt(LESS)));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
|
|
||||||
// Result is GREATER.
|
// Result is GREATER.
|
||||||
__ bind(&result_greater);
|
__ bind(&result_greater);
|
||||||
__ Set(eax, Immediate(Smi::FromInt(GREATER)));
|
__ Set(eax, Immediate(Smi::FromInt(GREATER)));
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -13544,6 +13682,10 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
|||||||
__ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
|
__ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
|
||||||
|
|
||||||
// Compare flat ascii strings.
|
// Compare flat ascii strings.
|
||||||
|
// Drop arguments from the stack.
|
||||||
|
__ pop(ecx);
|
||||||
|
__ add(Operand(esp), Immediate(2 * kPointerSize));
|
||||||
|
__ push(ecx);
|
||||||
GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
|
GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
|
||||||
|
|
||||||
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
|
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
|
||||||
|
12
deps/v8/src/ia32/codegen-ia32.h
vendored
12
deps/v8/src/ia32/codegen-ia32.h
vendored
@ -560,6 +560,17 @@ class CodeGenerator: public AstVisitor {
|
|||||||
Condition cc,
|
Condition cc,
|
||||||
bool strict,
|
bool strict,
|
||||||
ControlDestination* destination);
|
ControlDestination* destination);
|
||||||
|
|
||||||
|
// If at least one of the sides is a constant smi, generate optimized code.
|
||||||
|
void ConstantSmiComparison(Condition cc,
|
||||||
|
bool strict,
|
||||||
|
ControlDestination* destination,
|
||||||
|
Result* left_side,
|
||||||
|
Result* right_side,
|
||||||
|
bool left_side_constant_smi,
|
||||||
|
bool right_side_constant_smi,
|
||||||
|
bool is_loop_condition);
|
||||||
|
|
||||||
void GenerateInlineNumberComparison(Result* left_side,
|
void GenerateInlineNumberComparison(Result* left_side,
|
||||||
Result* right_side,
|
Result* right_side,
|
||||||
Condition cc,
|
Condition cc,
|
||||||
@ -621,6 +632,7 @@ class CodeGenerator: public AstVisitor {
|
|||||||
void GenerateIsArray(ZoneList<Expression*>* args);
|
void GenerateIsArray(ZoneList<Expression*>* args);
|
||||||
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
||||||
void GenerateIsObject(ZoneList<Expression*>* args);
|
void GenerateIsObject(ZoneList<Expression*>* args);
|
||||||
|
void GenerateIsSpecObject(ZoneList<Expression*>* args);
|
||||||
void GenerateIsFunction(ZoneList<Expression*>* args);
|
void GenerateIsFunction(ZoneList<Expression*>* args);
|
||||||
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
20
deps/v8/src/ia32/full-codegen-ia32.cc
vendored
20
deps/v8/src/ia32/full-codegen-ia32.cc
vendored
@ -1985,6 +1985,26 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
VisitForValue(args->at(0), kAccumulator);
|
||||||
|
|
||||||
|
Label materialize_true, materialize_false;
|
||||||
|
Label* if_true = NULL;
|
||||||
|
Label* if_false = NULL;
|
||||||
|
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
|
||||||
|
|
||||||
|
__ test(eax, Immediate(kSmiTagMask));
|
||||||
|
__ j(equal, if_false);
|
||||||
|
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ebx);
|
||||||
|
__ j(above_equal, if_true);
|
||||||
|
__ jmp(if_false);
|
||||||
|
|
||||||
|
Apply(context_, if_true, if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
||||||
ASSERT(args->length() == 1);
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
83
deps/v8/src/ia32/ic-ia32.cc
vendored
83
deps/v8/src/ia32/ic-ia32.cc
vendored
@ -1644,37 +1644,6 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
|||||||
// One byte opcode for test eax,0xXXXXXXXX.
|
// One byte opcode for test eax,0xXXXXXXXX.
|
||||||
static const byte kTestEaxByte = 0xA9;
|
static const byte kTestEaxByte = 0xA9;
|
||||||
|
|
||||||
|
|
||||||
void LoadIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Reset the map check of the inlined inobject property load (if
|
|
||||||
// present) to guarantee failure by holding an invalid map (the null
|
|
||||||
// value). The offset can be patched to anything.
|
|
||||||
PatchInlinedLoad(address, Heap::null_value(), kMaxInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedLoadIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Insert null as the map to check for to make sure the map check fails
|
|
||||||
// sending control flow to the IC instead of the inlined version.
|
|
||||||
PatchInlinedLoad(address, Heap::null_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Insert null as the elements map to check for. This will make
|
|
||||||
// sure that the elements fast-case map check fails so that control
|
|
||||||
// flows to the IC instead of the inlined version.
|
|
||||||
PatchInlinedStore(address, Heap::null_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::RestoreInlinedVersion(Address address) {
|
|
||||||
// Restore the fast-case elements map check so that the inlined
|
|
||||||
// version can be used again.
|
|
||||||
PatchInlinedStore(address, Heap::fixed_array_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
||||||
// The address of the instruction following the call.
|
// The address of the instruction following the call.
|
||||||
Address test_instruction_address =
|
Address test_instruction_address =
|
||||||
@ -1703,6 +1672,52 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
|
||||||
|
// The address of the instruction following the call.
|
||||||
|
Address test_instruction_address =
|
||||||
|
address + Assembler::kCallTargetAddressOffset;
|
||||||
|
|
||||||
|
// If the instruction following the call is not a test eax, nothing
|
||||||
|
// was inlined.
|
||||||
|
if (*test_instruction_address != kTestEaxByte) return false;
|
||||||
|
|
||||||
|
// Extract the encoded deltas from the test eax instruction.
|
||||||
|
Address encoded_offsets_address = test_instruction_address + 1;
|
||||||
|
int encoded_offsets = *reinterpret_cast<int*>(encoded_offsets_address);
|
||||||
|
int delta_to_map_check = -(encoded_offsets & 0xFFFF);
|
||||||
|
int delta_to_record_write = encoded_offsets >> 16;
|
||||||
|
|
||||||
|
// Patch the map to check. The map address is the last 4 bytes of
|
||||||
|
// the 7-byte operand-immediate compare instruction.
|
||||||
|
Address map_check_address = test_instruction_address + delta_to_map_check;
|
||||||
|
Address map_address = map_check_address + 3;
|
||||||
|
*(reinterpret_cast<Object**>(map_address)) = map;
|
||||||
|
|
||||||
|
// Patch the offset in the store instruction. The offset is in the
|
||||||
|
// last 4 bytes of a six byte register-to-memory move instruction.
|
||||||
|
Address offset_address =
|
||||||
|
map_check_address + StoreIC::kOffsetToStoreInstruction + 2;
|
||||||
|
// The offset should have initial value (kMaxInt - 1), cleared value
|
||||||
|
// (-1) or we should be clearing the inlined version.
|
||||||
|
ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt - 1 ||
|
||||||
|
*reinterpret_cast<int*>(offset_address) == -1 ||
|
||||||
|
(offset == 0 && map == Heap::null_value()));
|
||||||
|
*reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
|
||||||
|
|
||||||
|
// Patch the offset in the write-barrier code. The offset is the
|
||||||
|
// last 4 bytes of a six byte lea instruction.
|
||||||
|
offset_address = map_check_address + delta_to_record_write + 2;
|
||||||
|
// The offset should have initial value (kMaxInt), cleared value
|
||||||
|
// (-1) or we should be clearing the inlined version.
|
||||||
|
ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt ||
|
||||||
|
*reinterpret_cast<int*>(offset_address) == -1 ||
|
||||||
|
(offset == 0 && map == Heap::null_value()));
|
||||||
|
*reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool PatchInlinedMapCheck(Address address, Object* map) {
|
static bool PatchInlinedMapCheck(Address address, Object* map) {
|
||||||
Address test_instruction_address =
|
Address test_instruction_address =
|
||||||
address + Assembler::kCallTargetAddressOffset;
|
address + Assembler::kCallTargetAddressOffset;
|
||||||
@ -1814,6 +1829,12 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The offset from the inlined patch site to the start of the inlined
|
||||||
|
// store instruction. It is 7 bytes (test reg, imm) plus 6 bytes (jne
|
||||||
|
// slow_label).
|
||||||
|
const int StoreIC::kOffsetToStoreInstruction = 13;
|
||||||
|
|
||||||
|
|
||||||
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
// -- eax : value
|
// -- eax : value
|
||||||
|
6
deps/v8/src/ia32/macro-assembler-ia32.cc
vendored
6
deps/v8/src/ia32/macro-assembler-ia32.cc
vendored
@ -98,8 +98,10 @@ void MacroAssembler::InNewSpace(Register object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MacroAssembler::RecordWrite(Register object, int offset,
|
void MacroAssembler::RecordWrite(Register object,
|
||||||
Register value, Register scratch) {
|
int offset,
|
||||||
|
Register value,
|
||||||
|
Register scratch) {
|
||||||
// The compiled code assumes that record write doesn't change the
|
// The compiled code assumes that record write doesn't change the
|
||||||
// context register, so we check that none of the clobbered
|
// context register, so we check that none of the clobbered
|
||||||
// registers are esi.
|
// registers are esi.
|
||||||
|
2
deps/v8/src/ia32/virtual-frame-ia32.cc
vendored
2
deps/v8/src/ia32/virtual-frame-ia32.cc
vendored
@ -1034,7 +1034,7 @@ Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
|
|||||||
|
|
||||||
Result VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
|
Result VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
|
||||||
// Value and (if not contextual) receiver are on top of the frame.
|
// Value and (if not contextual) receiver are on top of the frame.
|
||||||
// The IC expects name in ecx, value in eax, and receiver in edx.
|
// The IC expects name in ecx, value in eax, and receiver in edx.
|
||||||
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
||||||
Result value = Pop();
|
Result value = Pop();
|
||||||
if (is_contextual) {
|
if (is_contextual) {
|
||||||
|
93
deps/v8/src/ic.cc
vendored
93
deps/v8/src/ic.cc
vendored
@ -277,6 +277,13 @@ void CallICBase::Clear(Address address, Code* target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void KeyedLoadIC::ClearInlinedVersion(Address address) {
|
||||||
|
// Insert null as the map to check for to make sure the map check fails
|
||||||
|
// sending control flow to the IC instead of the inlined version.
|
||||||
|
PatchInlinedLoad(address, Heap::null_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void KeyedLoadIC::Clear(Address address, Code* target) {
|
void KeyedLoadIC::Clear(Address address, Code* target) {
|
||||||
if (target->ic_state() == UNINITIALIZED) return;
|
if (target->ic_state() == UNINITIALIZED) return;
|
||||||
// Make sure to also clear the map used in inline fast cases. If we
|
// Make sure to also clear the map used in inline fast cases. If we
|
||||||
@ -287,6 +294,14 @@ void KeyedLoadIC::Clear(Address address, Code* target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LoadIC::ClearInlinedVersion(Address address) {
|
||||||
|
// Reset the map check of the inlined inobject property load (if
|
||||||
|
// present) to guarantee failure by holding an invalid map (the null
|
||||||
|
// value). The offset can be patched to anything.
|
||||||
|
PatchInlinedLoad(address, Heap::null_value(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LoadIC::Clear(Address address, Code* target) {
|
void LoadIC::Clear(Address address, Code* target) {
|
||||||
if (target->ic_state() == UNINITIALIZED) return;
|
if (target->ic_state() == UNINITIALIZED) return;
|
||||||
ClearInlinedVersion(address);
|
ClearInlinedVersion(address);
|
||||||
@ -294,12 +309,36 @@ void LoadIC::Clear(Address address, Code* target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StoreIC::ClearInlinedVersion(Address address) {
|
||||||
|
// Reset the map check of the inlined inobject property store (if
|
||||||
|
// present) to guarantee failure by holding an invalid map (the null
|
||||||
|
// value). The offset can be patched to anything.
|
||||||
|
PatchInlinedStore(address, Heap::null_value(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void StoreIC::Clear(Address address, Code* target) {
|
void StoreIC::Clear(Address address, Code* target) {
|
||||||
if (target->ic_state() == UNINITIALIZED) return;
|
if (target->ic_state() == UNINITIALIZED) return;
|
||||||
|
ClearInlinedVersion(address);
|
||||||
SetTargetAtAddress(address, initialize_stub());
|
SetTargetAtAddress(address, initialize_stub());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void KeyedStoreIC::ClearInlinedVersion(Address address) {
|
||||||
|
// Insert null as the elements map to check for. This will make
|
||||||
|
// sure that the elements fast-case map check fails so that control
|
||||||
|
// flows to the IC instead of the inlined version.
|
||||||
|
PatchInlinedStore(address, Heap::null_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void KeyedStoreIC::RestoreInlinedVersion(Address address) {
|
||||||
|
// Restore the fast-case elements map check so that the inlined
|
||||||
|
// version can be used again.
|
||||||
|
PatchInlinedStore(address, Heap::fixed_array_map());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::Clear(Address address, Code* target) {
|
void KeyedStoreIC::Clear(Address address, Code* target) {
|
||||||
if (target->ic_state() == UNINITIALIZED) return;
|
if (target->ic_state() == UNINITIALIZED) return;
|
||||||
SetTargetAtAddress(address, initialize_stub());
|
SetTargetAtAddress(address, initialize_stub());
|
||||||
@ -777,11 +816,13 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
|
|||||||
int offset = map->instance_size() + (index * kPointerSize);
|
int offset = map->instance_size() + (index * kPointerSize);
|
||||||
if (PatchInlinedLoad(address(), map, offset)) {
|
if (PatchInlinedLoad(address(), map, offset)) {
|
||||||
set_target(megamorphic_stub());
|
set_target(megamorphic_stub());
|
||||||
return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (FLAG_trace_ic) {
|
if (FLAG_trace_ic) {
|
||||||
PrintF("[LoadIC : inline patch %s]\n", *name->ToCString());
|
PrintF("[LoadIC : inline patch %s]\n", *name->ToCString());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
|
||||||
|
#ifdef DEBUG
|
||||||
} else {
|
} else {
|
||||||
if (FLAG_trace_ic) {
|
if (FLAG_trace_ic) {
|
||||||
PrintF("[LoadIC : no inline patch %s (patching failed)]\n",
|
PrintF("[LoadIC : no inline patch %s (patching failed)]\n",
|
||||||
@ -1205,7 +1246,57 @@ Object* StoreIC::Store(State state,
|
|||||||
// Lookup the property locally in the receiver.
|
// Lookup the property locally in the receiver.
|
||||||
if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
|
if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
|
||||||
LookupResult lookup;
|
LookupResult lookup;
|
||||||
|
|
||||||
if (LookupForWrite(*receiver, *name, &lookup)) {
|
if (LookupForWrite(*receiver, *name, &lookup)) {
|
||||||
|
bool can_be_inlined =
|
||||||
|
state == UNINITIALIZED &&
|
||||||
|
lookup.IsProperty() &&
|
||||||
|
lookup.holder() == *receiver &&
|
||||||
|
lookup.type() == FIELD &&
|
||||||
|
!receiver->IsAccessCheckNeeded();
|
||||||
|
|
||||||
|
if (can_be_inlined) {
|
||||||
|
Map* map = lookup.holder()->map();
|
||||||
|
// Property's index in the properties array. If negative we have
|
||||||
|
// an inobject property.
|
||||||
|
int index = lookup.GetFieldIndex() - map->inobject_properties();
|
||||||
|
if (index < 0) {
|
||||||
|
// Index is an offset from the end of the object.
|
||||||
|
int offset = map->instance_size() + (index * kPointerSize);
|
||||||
|
if (PatchInlinedStore(address(), map, offset)) {
|
||||||
|
set_target(megamorphic_stub());
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (FLAG_trace_ic) {
|
||||||
|
PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return receiver->SetProperty(*name, *value, NONE);
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (FLAG_trace_ic) {
|
||||||
|
PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
|
||||||
|
*name->ToCString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FLAG_trace_ic) {
|
||||||
|
PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
|
||||||
|
*name->ToCString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (state == PREMONOMORPHIC) {
|
||||||
|
if (FLAG_trace_ic) {
|
||||||
|
PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
|
||||||
|
*name->ToCString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no inlined store ic was patched, generate a stub for this
|
||||||
|
// store.
|
||||||
UpdateCaches(&lookup, state, receiver, name, value);
|
UpdateCaches(&lookup, state, receiver, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
deps/v8/src/ic.h
vendored
12
deps/v8/src/ic.h
vendored
@ -391,6 +391,13 @@ class StoreIC: public IC {
|
|||||||
static void GenerateArrayLength(MacroAssembler* masm);
|
static void GenerateArrayLength(MacroAssembler* masm);
|
||||||
static void GenerateNormal(MacroAssembler* masm);
|
static void GenerateNormal(MacroAssembler* masm);
|
||||||
|
|
||||||
|
// Clear the use of an inlined version.
|
||||||
|
static void ClearInlinedVersion(Address address);
|
||||||
|
|
||||||
|
// The offset from the inlined patch site to the start of the
|
||||||
|
// inlined store instruction.
|
||||||
|
static const int kOffsetToStoreInstruction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Update the inline cache and the global stub cache based on the
|
// Update the inline cache and the global stub cache based on the
|
||||||
// lookup result.
|
// lookup result.
|
||||||
@ -408,6 +415,11 @@ class StoreIC: public IC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void Clear(Address address, Code* target);
|
static void Clear(Address address, Code* target);
|
||||||
|
|
||||||
|
// Support for patching the index and the map that is checked in an
|
||||||
|
// inlined version of the named store.
|
||||||
|
static bool PatchInlinedStore(Address address, Object* map, int index);
|
||||||
|
|
||||||
friend class IC;
|
friend class IC;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
3
deps/v8/src/macros.py
vendored
3
deps/v8/src/macros.py
vendored
@ -115,7 +115,8 @@ macro FLOOR(arg) = $floor(arg);
|
|||||||
# Macro for ECMAScript 5 queries of the type:
|
# Macro for ECMAScript 5 queries of the type:
|
||||||
# "Type(O) is object."
|
# "Type(O) is object."
|
||||||
# This is the same as being either a function or an object in V8 terminology.
|
# This is the same as being either a function or an object in V8 terminology.
|
||||||
macro IS_SPEC_OBJECT_OR_NULL(arg) = (%_IsObject(arg) || %_IsFunction(arg));
|
# In addition, an undetectable object is also included by this.
|
||||||
|
macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
|
||||||
|
|
||||||
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
|
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
|
||||||
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
|
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
|
||||||
|
4
deps/v8/src/mark-compact.cc
vendored
4
deps/v8/src/mark-compact.cc
vendored
@ -28,6 +28,7 @@
|
|||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
#include "execution.h"
|
#include "execution.h"
|
||||||
|
#include "heap-profiler.h"
|
||||||
#include "global-handles.h"
|
#include "global-handles.h"
|
||||||
#include "ic-inl.h"
|
#include "ic-inl.h"
|
||||||
#include "mark-compact.h"
|
#include "mark-compact.h"
|
||||||
@ -2218,6 +2219,7 @@ int MarkCompactCollector::RelocateOldNonCodeObject(HeapObject* obj,
|
|||||||
if (copied_to->IsJSFunction()) {
|
if (copied_to->IsJSFunction()) {
|
||||||
PROFILE(FunctionMoveEvent(old_addr, new_addr));
|
PROFILE(FunctionMoveEvent(old_addr, new_addr));
|
||||||
}
|
}
|
||||||
|
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
|
||||||
|
|
||||||
return obj_size;
|
return obj_size;
|
||||||
}
|
}
|
||||||
@ -2264,6 +2266,7 @@ int MarkCompactCollector::RelocateCodeObject(HeapObject* obj) {
|
|||||||
// Notify the logger that compiled code has moved.
|
// Notify the logger that compiled code has moved.
|
||||||
PROFILE(CodeMoveEvent(old_addr, new_addr));
|
PROFILE(CodeMoveEvent(old_addr, new_addr));
|
||||||
}
|
}
|
||||||
|
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
|
||||||
|
|
||||||
return obj_size;
|
return obj_size;
|
||||||
}
|
}
|
||||||
@ -2308,6 +2311,7 @@ int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
|
|||||||
if (copied_to->IsJSFunction()) {
|
if (copied_to->IsJSFunction()) {
|
||||||
PROFILE(FunctionMoveEvent(old_addr, new_addr));
|
PROFILE(FunctionMoveEvent(old_addr, new_addr));
|
||||||
}
|
}
|
||||||
|
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
|
||||||
|
|
||||||
return obj_size;
|
return obj_size;
|
||||||
}
|
}
|
||||||
|
5
deps/v8/src/mips/codegen-mips.cc
vendored
5
deps/v8/src/mips/codegen-mips.cc
vendored
@ -907,6 +907,11 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
UNIMPLEMENTED_MIPS();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
|
||||||
UNIMPLEMENTED_MIPS();
|
UNIMPLEMENTED_MIPS();
|
||||||
}
|
}
|
||||||
|
1
deps/v8/src/mips/codegen-mips.h
vendored
1
deps/v8/src/mips/codegen-mips.h
vendored
@ -355,6 +355,7 @@ class CodeGenerator: public AstVisitor {
|
|||||||
void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
|
void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
void GenerateIsObject(ZoneList<Expression*>* args);
|
void GenerateIsObject(ZoneList<Expression*>* args);
|
||||||
|
void GenerateIsSpecObject(ZoneList<Expression*>* args);
|
||||||
void GenerateIsFunction(ZoneList<Expression*>* args);
|
void GenerateIsFunction(ZoneList<Expression*>* args);
|
||||||
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
||||||
void GenerateStringAdd(ZoneList<Expression*>* args);
|
void GenerateStringAdd(ZoneList<Expression*>* args);
|
||||||
|
40
deps/v8/src/objects.cc
vendored
40
deps/v8/src/objects.cc
vendored
@ -7338,6 +7338,46 @@ int HashTable<Shape, Key>::FindEntry(Key key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find entry for key otherwise return kNotFound.
|
||||||
|
int StringDictionary::FindEntry(String* key) {
|
||||||
|
if (!key->IsSymbol()) {
|
||||||
|
return HashTable<StringDictionaryShape, String*>::FindEntry(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized for symbol key. Knowledge of the key type allows:
|
||||||
|
// 1. Move the check if the key is a symbol out of the loop.
|
||||||
|
// 2. Avoid comparing hash codes in symbol to symbol comparision.
|
||||||
|
// 3. Detect a case when a dictionary key is not a symbol but the key is.
|
||||||
|
// In case of positive result the dictionary key may be replaced by
|
||||||
|
// the symbol with minimal performance penalty. It gives a chance to
|
||||||
|
// perform further lookups in code stubs (and significant performance boost
|
||||||
|
// a certain style of code).
|
||||||
|
|
||||||
|
// EnsureCapacity will guarantee the hash table is never full.
|
||||||
|
uint32_t capacity = Capacity();
|
||||||
|
uint32_t entry = FirstProbe(key->Hash(), capacity);
|
||||||
|
uint32_t count = 1;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int index = EntryToIndex(entry);
|
||||||
|
Object* element = get(index);
|
||||||
|
if (element->IsUndefined()) break; // Empty entry.
|
||||||
|
if (key == element) return entry;
|
||||||
|
if (!element->IsSymbol() &&
|
||||||
|
!element->IsNull() &&
|
||||||
|
String::cast(element)->Equals(key)) {
|
||||||
|
// Replace a non-symbol key by the equivalent symbol for faster further
|
||||||
|
// lookups.
|
||||||
|
set(index, key);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
|
||||||
|
entry = NextProbe(entry, count++, capacity);
|
||||||
|
}
|
||||||
|
return kNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Shape, typename Key>
|
template<typename Shape, typename Key>
|
||||||
Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
|
Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
|
||||||
int capacity = Capacity();
|
int capacity = Capacity();
|
||||||
|
12
deps/v8/src/objects.h
vendored
12
deps/v8/src/objects.h
vendored
@ -39,7 +39,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// All object types in the V8 JavaScript are described in this file.
|
// Most object types in the V8 JavaScript are described in this file.
|
||||||
//
|
//
|
||||||
// Inheritance hierarchy:
|
// Inheritance hierarchy:
|
||||||
// - Object
|
// - Object
|
||||||
@ -74,8 +74,8 @@
|
|||||||
// - CodeCacheHashTable
|
// - CodeCacheHashTable
|
||||||
// - MapCache
|
// - MapCache
|
||||||
// - Context
|
// - Context
|
||||||
// - GlobalContext
|
|
||||||
// - JSFunctionResultCache
|
// - JSFunctionResultCache
|
||||||
|
// - SerializedScopeInfo
|
||||||
// - String
|
// - String
|
||||||
// - SeqString
|
// - SeqString
|
||||||
// - SeqAsciiString
|
// - SeqAsciiString
|
||||||
@ -2012,7 +2012,7 @@ class HashTable: public FixedArray {
|
|||||||
static const int kMaxCapacity =
|
static const int kMaxCapacity =
|
||||||
(FixedArray::kMaxLength - kElementsStartOffset) / kEntrySize;
|
(FixedArray::kMaxLength - kElementsStartOffset) / kEntrySize;
|
||||||
|
|
||||||
// Find entry for key otherwise return -1.
|
// Find entry for key otherwise return kNotFound.
|
||||||
int FindEntry(Key key);
|
int FindEntry(Key key);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -2294,6 +2294,10 @@ class StringDictionary: public Dictionary<StringDictionaryShape, String*> {
|
|||||||
// For transforming properties of a JSObject.
|
// For transforming properties of a JSObject.
|
||||||
Object* TransformPropertiesToFastFor(JSObject* obj,
|
Object* TransformPropertiesToFastFor(JSObject* obj,
|
||||||
int unused_property_fields);
|
int unused_property_fields);
|
||||||
|
|
||||||
|
// Find entry for key otherwise return kNotFound. Optimzed version of
|
||||||
|
// HashTable::FindEntry.
|
||||||
|
int FindEntry(String* key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -2721,7 +2725,7 @@ class Code: public HeapObject {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NUMBER_OF_KINDS = KEYED_STORE_IC + 1
|
NUMBER_OF_KINDS = LAST_IC_KIND + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_DISASSEMBLER
|
#ifdef ENABLE_DISASSEMBLER
|
||||||
|
301
deps/v8/src/profile-generator.cc
vendored
301
deps/v8/src/profile-generator.cc
vendored
@ -74,6 +74,7 @@ void TokenEnumerator::TokenRemovedCallback(v8::Persistent<v8::Value> handle,
|
|||||||
void* parameter) {
|
void* parameter) {
|
||||||
reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
|
reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
|
||||||
Utils::OpenHandle(*handle).location());
|
Utils::OpenHandle(*handle).location());
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -181,8 +182,6 @@ void ProfileNode::Print(int indent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class DeleteNodesCallback {
|
class DeleteNodesCallback {
|
||||||
public:
|
public:
|
||||||
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
|
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
|
||||||
@ -194,8 +193,6 @@ class DeleteNodesCallback {
|
|||||||
void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
|
void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
ProfileTree::ProfileTree()
|
ProfileTree::ProfileTree()
|
||||||
: root_entry_(Logger::FUNCTION_TAG,
|
: root_entry_(Logger::FUNCTION_TAG,
|
||||||
@ -240,8 +237,6 @@ void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct NodesPair {
|
struct NodesPair {
|
||||||
NodesPair(ProfileNode* src, ProfileNode* dst)
|
NodesPair(ProfileNode* src, ProfileNode* dst)
|
||||||
: src(src), dst(dst) { }
|
: src(src), dst(dst) { }
|
||||||
@ -294,8 +289,6 @@ class FilteredCloneCallback {
|
|||||||
int security_token_id_;
|
int security_token_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
|
void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
|
||||||
ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
|
ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
|
||||||
FilteredCloneCallback cb(root_, security_token_id);
|
FilteredCloneCallback cb(root_, security_token_id);
|
||||||
@ -309,8 +302,6 @@ void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class Position {
|
class Position {
|
||||||
public:
|
public:
|
||||||
explicit Position(ProfileNode* node)
|
explicit Position(ProfileNode* node)
|
||||||
@ -328,8 +319,6 @@ class Position {
|
|||||||
int child_idx_;
|
int child_idx_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
// Non-recursive implementation of a depth-first post-order tree traversal.
|
// Non-recursive implementation of a depth-first post-order tree traversal.
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
@ -355,8 +344,6 @@ void ProfileTree::TraverseDepthFirst(Callback* callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class CalculateTotalTicksCallback {
|
class CalculateTotalTicksCallback {
|
||||||
public:
|
public:
|
||||||
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
|
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
|
||||||
@ -370,8 +357,6 @@ class CalculateTotalTicksCallback {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
void ProfileTree::CalculateTotalTicks() {
|
void ProfileTree::CalculateTotalTicks() {
|
||||||
CalculateTotalTicksCallback cb;
|
CalculateTotalTicksCallback cb;
|
||||||
@ -877,6 +862,11 @@ void HeapEntry::SetAutoIndexReference(HeapEntry* entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapEntry::SetUnidirAutoIndexReference(HeapEntry* entry) {
|
||||||
|
children_.Add(new HeapGraphEdge(next_auto_index_++, this, entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int HeapEntry::TotalSize() {
|
int HeapEntry::TotalSize() {
|
||||||
return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
|
return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
|
||||||
}
|
}
|
||||||
@ -888,12 +878,12 @@ int HeapEntry::NonSharedTotalSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int HeapEntry::CalculateTotalSize() {
|
template<class Visitor>
|
||||||
snapshot_->ClearPaint();
|
void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
|
||||||
List<HeapEntry*> list(10);
|
List<HeapEntry*> list(10);
|
||||||
list.Add(this);
|
list.Add(this);
|
||||||
total_size_ = self_size_;
|
|
||||||
this->PaintReachable();
|
this->PaintReachable();
|
||||||
|
visitor->Apply(this);
|
||||||
while (!list.is_empty()) {
|
while (!list.is_empty()) {
|
||||||
HeapEntry* entry = list.RemoveLast();
|
HeapEntry* entry = list.RemoveLast();
|
||||||
const int children_count = entry->children_.length();
|
const int children_count = entry->children_.length();
|
||||||
@ -902,15 +892,48 @@ int HeapEntry::CalculateTotalSize() {
|
|||||||
if (!child->painted_reachable()) {
|
if (!child->painted_reachable()) {
|
||||||
list.Add(child);
|
list.Add(child);
|
||||||
child->PaintReachable();
|
child->PaintReachable();
|
||||||
total_size_ += child->self_size_;
|
visitor->Apply(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total_size_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
class NullClass {
|
||||||
|
public:
|
||||||
|
void Apply(HeapEntry* entry) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
void HeapEntry::PaintAllReachable() {
|
||||||
|
NullClass null;
|
||||||
|
ApplyAndPaintAllReachable(&null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TotalSizeCalculator {
|
||||||
|
public:
|
||||||
|
TotalSizeCalculator()
|
||||||
|
: total_size_(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int total_size() const { return total_size_; }
|
||||||
|
|
||||||
|
void Apply(HeapEntry* entry) {
|
||||||
|
total_size_ += entry->self_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int total_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
int HeapEntry::CalculateTotalSize() {
|
||||||
|
snapshot_->ClearPaint();
|
||||||
|
TotalSizeCalculator calc;
|
||||||
|
ApplyAndPaintAllReachable(&calc);
|
||||||
|
total_size_ = calc.total_size();
|
||||||
|
return total_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class NonSharedSizeCalculator {
|
class NonSharedSizeCalculator {
|
||||||
public:
|
public:
|
||||||
@ -930,41 +953,26 @@ class NonSharedSizeCalculator {
|
|||||||
int non_shared_total_size_;
|
int non_shared_total_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int HeapEntry::CalculateNonSharedTotalSize() {
|
int HeapEntry::CalculateNonSharedTotalSize() {
|
||||||
// To calculate non-shared total size, first we paint all reachable
|
// To calculate non-shared total size, first we paint all reachable
|
||||||
// nodes in one color, then we paint all nodes reachable from other
|
// nodes in one color, then we paint all nodes reachable from other
|
||||||
// nodes with a different color. Then we consider only nodes painted
|
// nodes with a different color. Then we consider only nodes painted
|
||||||
// with the first color for caclulating the total size.
|
// with the first color for calculating the total size.
|
||||||
snapshot_->ClearPaint();
|
snapshot_->ClearPaint();
|
||||||
|
PaintAllReachable();
|
||||||
|
|
||||||
List<HeapEntry*> list(10);
|
List<HeapEntry*> list(10);
|
||||||
list.Add(this);
|
if (this != snapshot_->root()) {
|
||||||
this->PaintReachable();
|
list.Add(snapshot_->root());
|
||||||
|
snapshot_->root()->PaintReachableFromOthers();
|
||||||
|
}
|
||||||
while (!list.is_empty()) {
|
while (!list.is_empty()) {
|
||||||
HeapEntry* entry = list.RemoveLast();
|
HeapEntry* entry = list.RemoveLast();
|
||||||
const int children_count = entry->children_.length();
|
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) {
|
for (int i = 0; i < children_count; ++i) {
|
||||||
HeapEntry* child = entry->children_[i]->to();
|
HeapEntry* child = entry->children_[i]->to();
|
||||||
if (child != this && child->not_painted_reachable_from_others()) {
|
if (child != this && child->not_painted_reachable_from_others()) {
|
||||||
list2.Add(child);
|
list.Add(child);
|
||||||
child->PaintReachableFromOthers();
|
child->PaintReachableFromOthers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -972,7 +980,8 @@ int HeapEntry::CalculateNonSharedTotalSize() {
|
|||||||
|
|
||||||
NonSharedSizeCalculator calculator;
|
NonSharedSizeCalculator calculator;
|
||||||
snapshot_->IterateEntries(&calculator);
|
snapshot_->IterateEntries(&calculator);
|
||||||
return calculator.non_shared_total_size();
|
non_shared_total_size_ = calculator.non_shared_total_size();
|
||||||
|
return non_shared_total_size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1078,7 +1087,8 @@ void HeapEntry::CutEdges() {
|
|||||||
|
|
||||||
|
|
||||||
void HeapEntry::Print(int max_depth, int indent) {
|
void HeapEntry::Print(int max_depth, int indent) {
|
||||||
OS::Print("%6d %6d %6d ", self_size_, TotalSize(), NonSharedTotalSize());
|
OS::Print("%6d %6d %6d [%ld] ",
|
||||||
|
self_size_, TotalSize(), NonSharedTotalSize(), id_);
|
||||||
if (type_ != STRING) {
|
if (type_ != STRING) {
|
||||||
OS::Print("%s %.40s\n", TypeAsString(), name_);
|
OS::Print("%s %.40s\n", TypeAsString(), name_);
|
||||||
} else {
|
} else {
|
||||||
@ -1244,7 +1254,13 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
|||||||
: collection_(collection),
|
: collection_(collection),
|
||||||
title_(title),
|
title_(title),
|
||||||
uid_(uid),
|
uid_(uid),
|
||||||
root_(this) {
|
root_(this),
|
||||||
|
sorted_entries_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeapSnapshot::~HeapSnapshot() {
|
||||||
|
delete sorted_entries_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1355,6 +1371,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
|||||||
HeapEntry* entry = new HeapEntry(this,
|
HeapEntry* entry = new HeapEntry(this,
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
|
collection_->GetObjectId(object->address()),
|
||||||
GetObjectSize(object),
|
GetObjectSize(object),
|
||||||
GetObjectSecurityToken(object));
|
GetObjectSecurityToken(object));
|
||||||
entries_.Pair(object, entry);
|
entries_.Pair(object, entry);
|
||||||
@ -1381,8 +1398,6 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class EdgesCutter {
|
class EdgesCutter {
|
||||||
public:
|
public:
|
||||||
explicit EdgesCutter(int global_security_token)
|
explicit EdgesCutter(int global_security_token)
|
||||||
@ -1400,8 +1415,6 @@ class EdgesCutter {
|
|||||||
const int global_security_token_;
|
const int global_security_token_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
|
void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
|
||||||
EdgesCutter cutter(GetGlobalSecurityToken());
|
EdgesCutter cutter(GetGlobalSecurityToken());
|
||||||
entries_.Apply(&cutter);
|
entries_.Apply(&cutter);
|
||||||
@ -1454,13 +1467,129 @@ int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EntriesCollector {
|
||||||
|
public:
|
||||||
|
explicit EntriesCollector(List<HeapEntry*>* list) : list_(list) { }
|
||||||
|
void Apply(HeapEntry* entry) {
|
||||||
|
list_->Add(entry);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
List<HeapEntry*>* list_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static int SortByIds(const T* entry1_ptr,
|
||||||
|
const T* entry2_ptr) {
|
||||||
|
if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
|
||||||
|
return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
|
||||||
|
if (sorted_entries_ != NULL) return sorted_entries_;
|
||||||
|
sorted_entries_ = new List<HeapEntry*>(entries_.capacity());
|
||||||
|
EntriesCollector collector(sorted_entries_);
|
||||||
|
entries_.Apply(&collector);
|
||||||
|
sorted_entries_->Sort(SortByIds);
|
||||||
|
return sorted_entries_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
|
||||||
|
return collection_->CompareSnapshots(this, snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void HeapSnapshot::Print(int max_depth) {
|
void HeapSnapshot::Print(int max_depth) {
|
||||||
root_.Print(max_depth, 0);
|
root_.Print(max_depth, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeapObjectsMap::HeapObjectsMap()
|
||||||
|
: initial_fill_mode_(true),
|
||||||
|
next_id_(1),
|
||||||
|
entries_map_(AddressesMatch),
|
||||||
|
entries_(new List<EntryInfo>()) { }
|
||||||
|
|
||||||
|
|
||||||
|
HeapObjectsMap::~HeapObjectsMap() {
|
||||||
|
delete entries_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapObjectsMap::SnapshotGenerationFinished() {
|
||||||
|
initial_fill_mode_ = false;
|
||||||
|
RemoveDeadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t HeapObjectsMap::FindObject(Address addr) {
|
||||||
|
if (!initial_fill_mode_) {
|
||||||
|
uint64_t existing = FindEntry(addr);
|
||||||
|
if (existing != 0) return existing;
|
||||||
|
}
|
||||||
|
uint64_t id = next_id_++;
|
||||||
|
AddEntry(addr, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapObjectsMap::MoveObject(Address from, Address to) {
|
||||||
|
if (from == to) return;
|
||||||
|
HashMap::Entry* entry = entries_map_.Lookup(from, AddressHash(from), false);
|
||||||
|
if (entry != NULL) {
|
||||||
|
void* value = entry->value;
|
||||||
|
entries_map_.Remove(from, AddressHash(from));
|
||||||
|
entry = entries_map_.Lookup(to, AddressHash(to), true);
|
||||||
|
// We can have an entry at the new location, it is OK, as GC can overwrite
|
||||||
|
// dead objects with alive objects being moved.
|
||||||
|
entry->value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapObjectsMap::AddEntry(Address addr, uint64_t id) {
|
||||||
|
HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
|
||||||
|
ASSERT(entry->value == NULL);
|
||||||
|
entry->value = reinterpret_cast<void*>(entries_->length());
|
||||||
|
entries_->Add(EntryInfo(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t HeapObjectsMap::FindEntry(Address addr) {
|
||||||
|
HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
|
||||||
|
if (entry != NULL) {
|
||||||
|
int entry_index =
|
||||||
|
static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
|
||||||
|
EntryInfo& entry_info = entries_->at(entry_index);
|
||||||
|
entry_info.accessed = true;
|
||||||
|
return entry_info.id;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HeapObjectsMap::RemoveDeadEntries() {
|
||||||
|
List<EntryInfo>* new_entries = new List<EntryInfo>();
|
||||||
|
for (HashMap::Entry* entry = entries_map_.Start();
|
||||||
|
entry != NULL;
|
||||||
|
entry = entries_map_.Next(entry)) {
|
||||||
|
int entry_index =
|
||||||
|
static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
|
||||||
|
EntryInfo& entry_info = entries_->at(entry_index);
|
||||||
|
if (entry_info.accessed) {
|
||||||
|
entry->value = reinterpret_cast<void*>(new_entries->length());
|
||||||
|
new_entries->Add(EntryInfo(entry_info.id, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete entries_;
|
||||||
|
entries_ = new_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HeapSnapshotsCollection::HeapSnapshotsCollection()
|
HeapSnapshotsCollection::HeapSnapshotsCollection()
|
||||||
: snapshots_uids_(HeapSnapshotsMatch),
|
: is_tracking_objects_(false),
|
||||||
|
snapshots_uids_(HeapSnapshotsMatch),
|
||||||
token_enumerator_(new TokenEnumerator()) {
|
token_enumerator_(new TokenEnumerator()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1478,6 +1607,7 @@ HeapSnapshotsCollection::~HeapSnapshotsCollection() {
|
|||||||
|
|
||||||
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
|
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
|
||||||
unsigned uid) {
|
unsigned uid) {
|
||||||
|
is_tracking_objects_ = true; // Start watching for heap objects moves.
|
||||||
HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
|
HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
|
||||||
snapshots_.Add(snapshot);
|
snapshots_.Add(snapshot);
|
||||||
HashMap::Entry* entry =
|
HashMap::Entry* entry =
|
||||||
@ -1498,6 +1628,13 @@ HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
|
||||||
|
HeapSnapshot* snapshot1,
|
||||||
|
HeapSnapshot* snapshot2) {
|
||||||
|
return comparator_.Compare(snapshot1, snapshot2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
|
HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
|
||||||
: snapshot_(snapshot) {
|
: snapshot_(snapshot) {
|
||||||
}
|
}
|
||||||
@ -1630,6 +1767,64 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) {
|
||||||
|
delete *diff_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeapSnapshotsComparator::~HeapSnapshotsComparator() {
|
||||||
|
diffs_.Iterate(DeleteHeapSnapshotsDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
|
||||||
|
HeapSnapshot* snapshot2) {
|
||||||
|
HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
|
||||||
|
diffs_.Add(diff);
|
||||||
|
List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList();
|
||||||
|
List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList();
|
||||||
|
int i = 0, j = 0;
|
||||||
|
List<HeapEntry*> added_entries, deleted_entries;
|
||||||
|
while (i < entries1->length() && j < entries2->length()) {
|
||||||
|
uint64_t id1 = entries1->at(i)->id();
|
||||||
|
uint64_t id2 = entries2->at(j)->id();
|
||||||
|
if (id1 == id2) {
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
} else if (id1 < id2) {
|
||||||
|
HeapEntry* entry = entries1->at(i++);
|
||||||
|
deleted_entries.Add(entry);
|
||||||
|
} else {
|
||||||
|
HeapEntry* entry = entries2->at(j++);
|
||||||
|
added_entries.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i < entries1->length()) {
|
||||||
|
HeapEntry* entry = entries1->at(i++);
|
||||||
|
deleted_entries.Add(entry);
|
||||||
|
}
|
||||||
|
while (j < entries2->length()) {
|
||||||
|
HeapEntry* entry = entries2->at(j++);
|
||||||
|
added_entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot1->ClearPaint();
|
||||||
|
snapshot1->root()->PaintAllReachable();
|
||||||
|
for (int i = 0; i < deleted_entries.length(); ++i) {
|
||||||
|
HeapEntry* entry = deleted_entries[i];
|
||||||
|
if (entry->painted_reachable())
|
||||||
|
diff->AddDeletedEntry(entry);
|
||||||
|
}
|
||||||
|
snapshot2->ClearPaint();
|
||||||
|
snapshot2->root()->PaintAllReachable();
|
||||||
|
for (int i = 0; i < added_entries.length(); ++i) {
|
||||||
|
HeapEntry* entry = added_entries[i];
|
||||||
|
if (entry->painted_reachable())
|
||||||
|
diff->AddAddedEntry(entry);
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
|
||||||
#endif // ENABLE_LOGGING_AND_PROFILING
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||||
|
128
deps/v8/src/profile-generator.h
vendored
128
deps/v8/src/profile-generator.h
vendored
@ -74,7 +74,7 @@ class StringsStorage {
|
|||||||
reinterpret_cast<char*>(key2)) == 0;
|
reinterpret_cast<char*>(key2)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// String::Hash -> const char*
|
// Mapping of strings by String::Hash to const char* strings.
|
||||||
HashMap names_;
|
HashMap names_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(StringsStorage);
|
DISALLOW_COPY_AND_ASSIGN(StringsStorage);
|
||||||
@ -156,7 +156,7 @@ class ProfileNode {
|
|||||||
CodeEntry* entry_;
|
CodeEntry* entry_;
|
||||||
unsigned total_ticks_;
|
unsigned total_ticks_;
|
||||||
unsigned self_ticks_;
|
unsigned self_ticks_;
|
||||||
// CodeEntry* -> ProfileNode*
|
// Mapping from CodeEntry* to ProfileNode*
|
||||||
HashMap children_;
|
HashMap children_;
|
||||||
List<ProfileNode*> children_list_;
|
List<ProfileNode*> children_list_;
|
||||||
|
|
||||||
@ -312,11 +312,12 @@ class CpuProfilesCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StringsStorage function_and_resource_names_;
|
StringsStorage function_and_resource_names_;
|
||||||
// args_count -> char*
|
// Mapping from args_count (int) to char* strings.
|
||||||
List<char*> args_count_names_;
|
List<char*> args_count_names_;
|
||||||
List<CodeEntry*> code_entries_;
|
List<CodeEntry*> code_entries_;
|
||||||
List<List<CpuProfile*>* > profiles_by_token_;
|
List<List<CpuProfile*>* > profiles_by_token_;
|
||||||
// uid -> index
|
// Mapping from profiles' uids to indexes in the second nested list
|
||||||
|
// of profiles_by_token_.
|
||||||
HashMap profiles_uids_;
|
HashMap profiles_uids_;
|
||||||
|
|
||||||
// Accessed by VM thread and profile generator thread.
|
// Accessed by VM thread and profile generator thread.
|
||||||
@ -482,6 +483,7 @@ class HeapEntry {
|
|||||||
visited_(false),
|
visited_(false),
|
||||||
type_(INTERNAL),
|
type_(INTERNAL),
|
||||||
name_(""),
|
name_(""),
|
||||||
|
id_(0),
|
||||||
next_auto_index_(0),
|
next_auto_index_(0),
|
||||||
self_size_(0),
|
self_size_(0),
|
||||||
security_token_id_(TokenEnumerator::kNoSecurityToken),
|
security_token_id_(TokenEnumerator::kNoSecurityToken),
|
||||||
@ -494,12 +496,14 @@ class HeapEntry {
|
|||||||
HeapEntry(HeapSnapshot* snapshot,
|
HeapEntry(HeapSnapshot* snapshot,
|
||||||
Type type,
|
Type type,
|
||||||
const char* name,
|
const char* name,
|
||||||
|
uint64_t id,
|
||||||
int self_size,
|
int self_size,
|
||||||
int security_token_id)
|
int security_token_id)
|
||||||
: snapshot_(snapshot),
|
: snapshot_(snapshot),
|
||||||
visited_(false),
|
visited_(false),
|
||||||
type_(type),
|
type_(type),
|
||||||
name_(name),
|
name_(name),
|
||||||
|
id_(id),
|
||||||
next_auto_index_(1),
|
next_auto_index_(1),
|
||||||
self_size_(self_size),
|
self_size_(self_size),
|
||||||
security_token_id_(security_token_id),
|
security_token_id_(security_token_id),
|
||||||
@ -514,6 +518,7 @@ class HeapEntry {
|
|||||||
bool visited() const { return visited_; }
|
bool visited() const { return visited_; }
|
||||||
Type type() const { return type_; }
|
Type type() const { return type_; }
|
||||||
const char* name() const { return name_; }
|
const char* name() const { return name_; }
|
||||||
|
uint64_t id() const { return id_; }
|
||||||
int self_size() const { return self_size_; }
|
int self_size() const { return self_size_; }
|
||||||
int security_token_id() const { return security_token_id_; }
|
int security_token_id() const { return security_token_id_; }
|
||||||
bool painted_reachable() { return painted_ == kPaintReachable; }
|
bool painted_reachable() { return painted_ == kPaintReachable; }
|
||||||
@ -524,9 +529,13 @@ class HeapEntry {
|
|||||||
const List<HeapGraphEdge*>* retainers() const { return &retainers_; }
|
const List<HeapGraphEdge*>* retainers() const { return &retainers_; }
|
||||||
const List<HeapGraphPath*>* GetRetainingPaths();
|
const List<HeapGraphPath*>* GetRetainingPaths();
|
||||||
|
|
||||||
|
template<class Visitor>
|
||||||
|
void ApplyAndPaintAllReachable(Visitor* visitor);
|
||||||
|
|
||||||
void ClearPaint() { painted_ = kUnpainted; }
|
void ClearPaint() { painted_ = kUnpainted; }
|
||||||
void CutEdges();
|
void CutEdges();
|
||||||
void MarkAsVisited() { visited_ = true; }
|
void MarkAsVisited() { visited_ = true; }
|
||||||
|
void PaintAllReachable();
|
||||||
void PaintReachable() {
|
void PaintReachable() {
|
||||||
ASSERT(painted_ == kUnpainted);
|
ASSERT(painted_ == kUnpainted);
|
||||||
painted_ = kPaintReachable;
|
painted_ = kPaintReachable;
|
||||||
@ -537,6 +546,7 @@ class HeapEntry {
|
|||||||
void SetInternalReference(const char* name, HeapEntry* entry);
|
void SetInternalReference(const char* name, HeapEntry* entry);
|
||||||
void SetPropertyReference(const char* name, HeapEntry* entry);
|
void SetPropertyReference(const char* name, HeapEntry* entry);
|
||||||
void SetAutoIndexReference(HeapEntry* entry);
|
void SetAutoIndexReference(HeapEntry* entry);
|
||||||
|
void SetUnidirAutoIndexReference(HeapEntry* entry);
|
||||||
|
|
||||||
int TotalSize();
|
int TotalSize();
|
||||||
int NonSharedTotalSize();
|
int NonSharedTotalSize();
|
||||||
@ -557,6 +567,7 @@ class HeapEntry {
|
|||||||
bool visited_;
|
bool visited_;
|
||||||
Type type_;
|
Type type_;
|
||||||
const char* name_;
|
const char* name_;
|
||||||
|
uint64_t id_;
|
||||||
int next_auto_index_;
|
int next_auto_index_;
|
||||||
int self_size_;
|
int self_size_;
|
||||||
int security_token_id_;
|
int security_token_id_;
|
||||||
@ -607,6 +618,8 @@ class HeapEntriesMap {
|
|||||||
HeapEntry* Map(HeapObject* object);
|
HeapEntry* Map(HeapObject* object);
|
||||||
void Pair(HeapObject* object, HeapEntry* entry);
|
void Pair(HeapObject* object, HeapEntry* entry);
|
||||||
|
|
||||||
|
uint32_t capacity() { return entries_.capacity(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
INLINE(uint32_t Hash(HeapObject* object)) {
|
INLINE(uint32_t Hash(HeapObject* object)) {
|
||||||
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
|
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
|
||||||
@ -627,6 +640,7 @@ class HeapEntriesMap {
|
|||||||
|
|
||||||
|
|
||||||
class HeapSnapshotsCollection;
|
class HeapSnapshotsCollection;
|
||||||
|
class HeapSnapshotsDiff;
|
||||||
|
|
||||||
// HeapSnapshot represents a single heap snapshot. It is stored in
|
// HeapSnapshot represents a single heap snapshot. It is stored in
|
||||||
// HeapSnapshotsCollection, which is also a factory for
|
// HeapSnapshotsCollection, which is also a factory for
|
||||||
@ -638,6 +652,7 @@ class HeapSnapshot {
|
|||||||
HeapSnapshot(HeapSnapshotsCollection* collection,
|
HeapSnapshot(HeapSnapshotsCollection* collection,
|
||||||
const char* title,
|
const char* title,
|
||||||
unsigned uid);
|
unsigned uid);
|
||||||
|
~HeapSnapshot();
|
||||||
void ClearPaint();
|
void ClearPaint();
|
||||||
void CutObjectsFromForeignSecurityContexts();
|
void CutObjectsFromForeignSecurityContexts();
|
||||||
HeapEntry* GetEntry(Object* object);
|
HeapEntry* GetEntry(Object* object);
|
||||||
@ -655,6 +670,8 @@ class HeapSnapshot {
|
|||||||
HeapEntry* root() { return &root_; }
|
HeapEntry* root() { return &root_; }
|
||||||
template<class Visitor>
|
template<class Visitor>
|
||||||
void IterateEntries(Visitor* visitor) { entries_.Apply(visitor); }
|
void IterateEntries(Visitor* visitor) { entries_.Apply(visitor); }
|
||||||
|
List<HeapEntry*>* GetSortedEntriesList();
|
||||||
|
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
|
||||||
|
|
||||||
void Print(int max_depth);
|
void Print(int max_depth);
|
||||||
|
|
||||||
@ -679,19 +696,108 @@ class HeapSnapshot {
|
|||||||
const char* title_;
|
const char* title_;
|
||||||
unsigned uid_;
|
unsigned uid_;
|
||||||
HeapEntry root_;
|
HeapEntry root_;
|
||||||
// HeapObject* -> HeapEntry*
|
// Mapping from HeapObject* pointers to HeapEntry* pointers.
|
||||||
HeapEntriesMap entries_;
|
HeapEntriesMap entries_;
|
||||||
|
// Entries sorted by id.
|
||||||
|
List<HeapEntry*>* sorted_entries_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
|
DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HeapObjectsMap {
|
||||||
|
public:
|
||||||
|
HeapObjectsMap();
|
||||||
|
~HeapObjectsMap();
|
||||||
|
|
||||||
|
void SnapshotGenerationFinished();
|
||||||
|
uint64_t FindObject(Address addr);
|
||||||
|
void MoveObject(Address from, Address to);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct EntryInfo {
|
||||||
|
explicit EntryInfo(uint64_t id) : id(id), accessed(true) { }
|
||||||
|
EntryInfo(uint64_t id, bool accessed) : id(id), accessed(accessed) { }
|
||||||
|
uint64_t id;
|
||||||
|
bool accessed;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddEntry(Address addr, uint64_t id);
|
||||||
|
uint64_t FindEntry(Address addr);
|
||||||
|
void RemoveDeadEntries();
|
||||||
|
|
||||||
|
static bool AddressesMatch(void* key1, void* key2) {
|
||||||
|
return key1 == key2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t AddressHash(Address addr) {
|
||||||
|
return static_cast<int32_t>(reinterpret_cast<intptr_t>(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initial_fill_mode_;
|
||||||
|
uint64_t next_id_;
|
||||||
|
HashMap entries_map_;
|
||||||
|
List<EntryInfo>* entries_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HeapSnapshotsDiff {
|
||||||
|
public:
|
||||||
|
HeapSnapshotsDiff(HeapSnapshot* snapshot1, HeapSnapshot* snapshot2)
|
||||||
|
: snapshot1_(snapshot1),
|
||||||
|
snapshot2_(snapshot2),
|
||||||
|
additions_root_(new HeapEntry(snapshot2)),
|
||||||
|
deletions_root_(new HeapEntry(snapshot1)) { }
|
||||||
|
|
||||||
|
~HeapSnapshotsDiff() {
|
||||||
|
delete deletions_root_;
|
||||||
|
delete additions_root_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddAddedEntry(HeapEntry* entry) {
|
||||||
|
additions_root_->SetUnidirAutoIndexReference(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDeletedEntry(HeapEntry* entry) {
|
||||||
|
deletions_root_->SetUnidirAutoIndexReference(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeapEntry* additions_root() const { return additions_root_; }
|
||||||
|
const HeapEntry* deletions_root() const { return deletions_root_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
HeapSnapshot* snapshot1_;
|
||||||
|
HeapSnapshot* snapshot2_;
|
||||||
|
HeapEntry* additions_root_;
|
||||||
|
HeapEntry* deletions_root_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsDiff);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HeapSnapshotsComparator {
|
||||||
|
public:
|
||||||
|
HeapSnapshotsComparator() { }
|
||||||
|
~HeapSnapshotsComparator();
|
||||||
|
HeapSnapshotsDiff* Compare(HeapSnapshot* snapshot1, HeapSnapshot* snapshot2);
|
||||||
|
private:
|
||||||
|
List<HeapSnapshotsDiff*> diffs_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsComparator);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class HeapSnapshotsCollection {
|
class HeapSnapshotsCollection {
|
||||||
public:
|
public:
|
||||||
HeapSnapshotsCollection();
|
HeapSnapshotsCollection();
|
||||||
~HeapSnapshotsCollection();
|
~HeapSnapshotsCollection();
|
||||||
|
|
||||||
|
bool is_tracking_objects() { return is_tracking_objects_; }
|
||||||
|
|
||||||
HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
|
HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
|
||||||
|
void SnapshotGenerationFinished() { ids_.SnapshotGenerationFinished(); }
|
||||||
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
|
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
|
||||||
HeapSnapshot* GetSnapshot(unsigned uid);
|
HeapSnapshot* GetSnapshot(unsigned uid);
|
||||||
|
|
||||||
@ -699,16 +805,26 @@ class HeapSnapshotsCollection {
|
|||||||
|
|
||||||
TokenEnumerator* token_enumerator() { return token_enumerator_; }
|
TokenEnumerator* token_enumerator() { return token_enumerator_; }
|
||||||
|
|
||||||
|
uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
|
||||||
|
void ObjectMoveEvent(Address from, Address to) { ids_.MoveObject(from, to); }
|
||||||
|
|
||||||
|
HeapSnapshotsDiff* CompareSnapshots(HeapSnapshot* snapshot1,
|
||||||
|
HeapSnapshot* snapshot2);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
INLINE(static bool HeapSnapshotsMatch(void* key1, void* key2)) {
|
INLINE(static bool HeapSnapshotsMatch(void* key1, void* key2)) {
|
||||||
return key1 == key2;
|
return key1 == key2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_tracking_objects_; // Whether tracking object moves is needed.
|
||||||
List<HeapSnapshot*> snapshots_;
|
List<HeapSnapshot*> snapshots_;
|
||||||
// uid -> HeapSnapshot*
|
// Mapping from snapshots' uids to HeapSnapshot* pointers.
|
||||||
HashMap snapshots_uids_;
|
HashMap snapshots_uids_;
|
||||||
StringsStorage names_;
|
StringsStorage names_;
|
||||||
TokenEnumerator* token_enumerator_;
|
TokenEnumerator* token_enumerator_;
|
||||||
|
// Mapping from HeapObject addresses to objects' uids.
|
||||||
|
HeapObjectsMap ids_;
|
||||||
|
HeapSnapshotsComparator comparator_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsCollection);
|
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsCollection);
|
||||||
};
|
};
|
||||||
|
22
deps/v8/src/runtime.js
vendored
22
deps/v8/src/runtime.js
vendored
@ -80,7 +80,7 @@ function EQUALS(y) {
|
|||||||
} else {
|
} else {
|
||||||
// x is not a number, boolean, null or undefined.
|
// x is not a number, boolean, null or undefined.
|
||||||
if (y == null) return 1; // not equal
|
if (y == null) return 1; // not equal
|
||||||
if (IS_SPEC_OBJECT_OR_NULL(y)) {
|
if (IS_SPEC_OBJECT(y)) {
|
||||||
return %_ObjectEquals(x, y) ? 0 : 1;
|
return %_ObjectEquals(x, y) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +345,7 @@ function DELETE(key) {
|
|||||||
|
|
||||||
// ECMA-262, section 11.8.7, page 54.
|
// ECMA-262, section 11.8.7, page 54.
|
||||||
function IN(x) {
|
function IN(x) {
|
||||||
if (x == null || !IS_SPEC_OBJECT_OR_NULL(x)) {
|
if (!IS_SPEC_OBJECT(x)) {
|
||||||
throw %MakeTypeError('invalid_in_operator_use', [this, x]);
|
throw %MakeTypeError('invalid_in_operator_use', [this, x]);
|
||||||
}
|
}
|
||||||
return %_IsNonNegativeSmi(this) ? %HasElement(x, this) : %HasProperty(x, %ToString(this));
|
return %_IsNonNegativeSmi(this) ? %HasElement(x, this) : %HasProperty(x, %ToString(this));
|
||||||
@ -363,13 +363,13 @@ function INSTANCE_OF(F) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If V is not an object, return false.
|
// If V is not an object, return false.
|
||||||
if (IS_NULL(V) || !IS_SPEC_OBJECT_OR_NULL(V)) {
|
if (!IS_SPEC_OBJECT(V)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the prototype of F; if it is not an object, throw an error.
|
// Get the prototype of F; if it is not an object, throw an error.
|
||||||
var O = F.prototype;
|
var O = F.prototype;
|
||||||
if (IS_NULL(O) || !IS_SPEC_OBJECT_OR_NULL(O)) {
|
if (!IS_SPEC_OBJECT(O)) {
|
||||||
throw %MakeTypeError('instanceof_nonobject_proto', [O]);
|
throw %MakeTypeError('instanceof_nonobject_proto', [O]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,8 +483,7 @@ function ToPrimitive(x, hint) {
|
|||||||
// Fast case check.
|
// Fast case check.
|
||||||
if (IS_STRING(x)) return x;
|
if (IS_STRING(x)) return x;
|
||||||
// Normal behavior.
|
// Normal behavior.
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(x)) return x;
|
if (!IS_SPEC_OBJECT(x)) return x;
|
||||||
if (x == null) return x; // check for null, undefined
|
|
||||||
if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
|
if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
|
||||||
return (hint == NUMBER_HINT) ? %DefaultNumber(x) : %DefaultString(x);
|
return (hint == NUMBER_HINT) ? %DefaultNumber(x) : %DefaultString(x);
|
||||||
}
|
}
|
||||||
@ -583,13 +582,10 @@ function SameValue(x, y) {
|
|||||||
// Returns if the given x is a primitive value - not an object or a
|
// Returns if the given x is a primitive value - not an object or a
|
||||||
// function.
|
// function.
|
||||||
function IsPrimitive(x) {
|
function IsPrimitive(x) {
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(x)) {
|
// Even though the type of null is "object", null is still
|
||||||
return true;
|
// considered a primitive value. IS_SPEC_OBJECT handles this correctly
|
||||||
} else {
|
// (i.e., it will return false if x is null).
|
||||||
// Even though the type of null is "object", null is still
|
return !IS_SPEC_OBJECT(x);
|
||||||
// considered a primitive value.
|
|
||||||
return IS_NULL(x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
2
deps/v8/src/serialize.cc
vendored
2
deps/v8/src/serialize.cc
vendored
@ -360,6 +360,7 @@ void ExternalReferenceTable::PopulateTable() {
|
|||||||
UNCLASSIFIED,
|
UNCLASSIFIED,
|
||||||
5,
|
5,
|
||||||
"StackGuard::address_of_real_jslimit()");
|
"StackGuard::address_of_real_jslimit()");
|
||||||
|
#ifndef V8_INTERPRETED_REGEXP
|
||||||
Add(ExternalReference::address_of_regexp_stack_limit().address(),
|
Add(ExternalReference::address_of_regexp_stack_limit().address(),
|
||||||
UNCLASSIFIED,
|
UNCLASSIFIED,
|
||||||
6,
|
6,
|
||||||
@ -376,6 +377,7 @@ void ExternalReferenceTable::PopulateTable() {
|
|||||||
UNCLASSIFIED,
|
UNCLASSIFIED,
|
||||||
9,
|
9,
|
||||||
"OffsetsVector::static_offsets_vector");
|
"OffsetsVector::static_offsets_vector");
|
||||||
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
Add(ExternalReference::new_space_start().address(),
|
Add(ExternalReference::new_space_start().address(),
|
||||||
UNCLASSIFIED,
|
UNCLASSIFIED,
|
||||||
10,
|
10,
|
||||||
|
4
deps/v8/src/spaces.cc
vendored
4
deps/v8/src/spaces.cc
vendored
@ -342,7 +342,9 @@ void MemoryAllocator::TearDown() {
|
|||||||
void* MemoryAllocator::AllocateRawMemory(const size_t requested,
|
void* MemoryAllocator::AllocateRawMemory(const size_t requested,
|
||||||
size_t* allocated,
|
size_t* allocated,
|
||||||
Executability executable) {
|
Executability executable) {
|
||||||
if (size_ + static_cast<int>(requested) > capacity_) return NULL;
|
if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
void* mem;
|
void* mem;
|
||||||
if (executable == EXECUTABLE && CodeRange::exists()) {
|
if (executable == EXECUTABLE && CodeRange::exists()) {
|
||||||
mem = CodeRange::AllocateRawMemory(requested, allocated);
|
mem = CodeRange::AllocateRawMemory(requested, allocated);
|
||||||
|
46
deps/v8/src/v8natives.js
vendored
46
deps/v8/src/v8natives.js
vendored
@ -225,16 +225,14 @@ function ObjectHasOwnProperty(V) {
|
|||||||
|
|
||||||
// ECMA-262 - 15.2.4.6
|
// ECMA-262 - 15.2.4.6
|
||||||
function ObjectIsPrototypeOf(V) {
|
function ObjectIsPrototypeOf(V) {
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(V) && !IS_UNDETECTABLE(V)) return false;
|
if (!IS_SPEC_OBJECT(V)) return false;
|
||||||
return %IsInPrototypeChain(this, V);
|
return %IsInPrototypeChain(this, V);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ECMA-262 - 15.2.4.6
|
// ECMA-262 - 15.2.4.6
|
||||||
function ObjectPropertyIsEnumerable(V) {
|
function ObjectPropertyIsEnumerable(V) {
|
||||||
if (this == null) return false;
|
return %IsPropertyEnumerable(ToObject(this), ToString(V));
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(this)) return false;
|
|
||||||
return %IsPropertyEnumerable(this, ToString(V));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -279,8 +277,7 @@ function ObjectLookupSetter(name) {
|
|||||||
|
|
||||||
|
|
||||||
function ObjectKeys(obj) {
|
function ObjectKeys(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj))
|
||||||
!IS_UNDETECTABLE(obj))
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
|
||||||
return %LocalKeys(obj);
|
return %LocalKeys(obj);
|
||||||
}
|
}
|
||||||
@ -329,7 +326,7 @@ function FromPropertyDescriptor(desc) {
|
|||||||
|
|
||||||
// ES5 8.10.5.
|
// ES5 8.10.5.
|
||||||
function ToPropertyDescriptor(obj) {
|
function ToPropertyDescriptor(obj) {
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(obj)) {
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
throw MakeTypeError("property_desc_object", [obj]);
|
throw MakeTypeError("property_desc_object", [obj]);
|
||||||
}
|
}
|
||||||
var desc = new PropertyDescriptor();
|
var desc = new PropertyDescriptor();
|
||||||
@ -626,8 +623,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.2.
|
// ES5 section 15.2.3.2.
|
||||||
function ObjectGetPrototypeOf(obj) {
|
function ObjectGetPrototypeOf(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj))
|
||||||
!IS_UNDETECTABLE(obj))
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
|
||||||
return obj.__proto__;
|
return obj.__proto__;
|
||||||
}
|
}
|
||||||
@ -635,8 +631,7 @@ function ObjectGetPrototypeOf(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.3
|
// ES5 section 15.2.3.3
|
||||||
function ObjectGetOwnPropertyDescriptor(obj, p) {
|
function ObjectGetOwnPropertyDescriptor(obj, p) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj))
|
||||||
!IS_UNDETECTABLE(obj))
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
|
||||||
var desc = GetOwnProperty(obj, p);
|
var desc = GetOwnProperty(obj, p);
|
||||||
return FromPropertyDescriptor(desc);
|
return FromPropertyDescriptor(desc);
|
||||||
@ -645,8 +640,7 @@ function ObjectGetOwnPropertyDescriptor(obj, p) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.4.
|
// ES5 section 15.2.3.4.
|
||||||
function ObjectGetOwnPropertyNames(obj) {
|
function ObjectGetOwnPropertyNames(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj))
|
||||||
!IS_UNDETECTABLE(obj))
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
|
||||||
|
|
||||||
// Find all the indexed properties.
|
// Find all the indexed properties.
|
||||||
@ -698,7 +692,7 @@ function ObjectGetOwnPropertyNames(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.5.
|
// ES5 section 15.2.3.5.
|
||||||
function ObjectCreate(proto, properties) {
|
function ObjectCreate(proto, properties) {
|
||||||
if (!IS_SPEC_OBJECT_OR_NULL(proto)) {
|
if (!IS_SPEC_OBJECT(proto) && proto !== null) {
|
||||||
throw MakeTypeError("proto_object_or_null", [proto]);
|
throw MakeTypeError("proto_object_or_null", [proto]);
|
||||||
}
|
}
|
||||||
var obj = new $Object();
|
var obj = new $Object();
|
||||||
@ -710,8 +704,7 @@ function ObjectCreate(proto, properties) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.6.
|
// ES5 section 15.2.3.6.
|
||||||
function ObjectDefineProperty(obj, p, attributes) {
|
function ObjectDefineProperty(obj, p, attributes) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
|
||||||
}
|
}
|
||||||
var name = ToString(p);
|
var name = ToString(p);
|
||||||
@ -723,8 +716,7 @@ function ObjectDefineProperty(obj, p, attributes) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.7.
|
// ES5 section 15.2.3.7.
|
||||||
function ObjectDefineProperties(obj, properties) {
|
function ObjectDefineProperties(obj, properties) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj))
|
||||||
!IS_UNDETECTABLE(obj))
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
|
||||||
var props = ToObject(properties);
|
var props = ToObject(properties);
|
||||||
var key_values = [];
|
var key_values = [];
|
||||||
@ -747,8 +739,7 @@ function ObjectDefineProperties(obj, properties) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.8.
|
// ES5 section 15.2.3.8.
|
||||||
function ObjectSeal(obj) {
|
function ObjectSeal(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
|
||||||
}
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
@ -764,8 +755,7 @@ function ObjectSeal(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.9.
|
// ES5 section 15.2.3.9.
|
||||||
function ObjectFreeze(obj) {
|
function ObjectFreeze(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
|
||||||
}
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
@ -782,8 +772,7 @@ function ObjectFreeze(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.10
|
// ES5 section 15.2.3.10
|
||||||
function ObjectPreventExtension(obj) {
|
function ObjectPreventExtension(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
||||||
}
|
}
|
||||||
%PreventExtensions(obj);
|
%PreventExtensions(obj);
|
||||||
@ -793,8 +782,7 @@ function ObjectPreventExtension(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.11
|
// ES5 section 15.2.3.11
|
||||||
function ObjectIsSealed(obj) {
|
function ObjectIsSealed(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
|
||||||
}
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
@ -812,8 +800,7 @@ function ObjectIsSealed(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.12
|
// ES5 section 15.2.3.12
|
||||||
function ObjectIsFrozen(obj) {
|
function ObjectIsFrozen(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
|
||||||
}
|
}
|
||||||
var names = ObjectGetOwnPropertyNames(obj);
|
var names = ObjectGetOwnPropertyNames(obj);
|
||||||
@ -832,8 +819,7 @@ function ObjectIsFrozen(obj) {
|
|||||||
|
|
||||||
// ES5 section 15.2.3.13
|
// ES5 section 15.2.3.13
|
||||||
function ObjectIsExtensible(obj) {
|
function ObjectIsExtensible(obj) {
|
||||||
if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
|
if (!IS_SPEC_OBJECT(obj)) {
|
||||||
!IS_UNDETECTABLE(obj)) {
|
|
||||||
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
|
||||||
}
|
}
|
||||||
return %IsExtensible(obj);
|
return %IsExtensible(obj);
|
||||||
|
2
deps/v8/src/version.cc
vendored
2
deps/v8/src/version.cc
vendored
@ -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 3
|
#define MINOR_VERSION 3
|
||||||
#define BUILD_NUMBER 0
|
#define BUILD_NUMBER 2
|
||||||
#define PATCH_LEVEL 0
|
#define PATCH_LEVEL 0
|
||||||
#define CANDIDATE_VERSION false
|
#define CANDIDATE_VERSION false
|
||||||
|
|
||||||
|
721
deps/v8/src/x64/codegen-x64.cc
vendored
721
deps/v8/src/x64/codegen-x64.cc
vendored
File diff suppressed because it is too large
Load Diff
20
deps/v8/src/x64/codegen-x64.h
vendored
20
deps/v8/src/x64/codegen-x64.h
vendored
@ -454,9 +454,17 @@ class CodeGenerator: public AstVisitor {
|
|||||||
// value in place.
|
// value in place.
|
||||||
void StoreToSlot(Slot* slot, InitState init_state);
|
void StoreToSlot(Slot* slot, InitState init_state);
|
||||||
|
|
||||||
|
// Support for compiling assignment expressions.
|
||||||
|
void EmitSlotAssignment(Assignment* node);
|
||||||
|
void EmitNamedPropertyAssignment(Assignment* node);
|
||||||
|
|
||||||
// Receiver is passed on the frame and not consumed.
|
// Receiver is passed on the frame and not consumed.
|
||||||
Result EmitNamedLoad(Handle<String> name, bool is_contextual);
|
Result EmitNamedLoad(Handle<String> name, bool is_contextual);
|
||||||
|
|
||||||
|
// If the store is contextual, value is passed on the frame and consumed.
|
||||||
|
// Otherwise, receiver and value are passed on the frame and consumed.
|
||||||
|
Result EmitNamedStore(Handle<String> name, bool is_contextual);
|
||||||
|
|
||||||
// Load a property of an object, returning it in a Result.
|
// Load a property of an object, returning it in a Result.
|
||||||
// The object and the property name are passed on the stack, and
|
// The object and the property name are passed on the stack, and
|
||||||
// not changed.
|
// not changed.
|
||||||
@ -521,6 +529,17 @@ class CodeGenerator: public AstVisitor {
|
|||||||
Condition cc,
|
Condition cc,
|
||||||
bool strict,
|
bool strict,
|
||||||
ControlDestination* destination);
|
ControlDestination* destination);
|
||||||
|
|
||||||
|
// If at least one of the sides is a constant smi, generate optimized code.
|
||||||
|
void ConstantSmiComparison(Condition cc,
|
||||||
|
bool strict,
|
||||||
|
ControlDestination* destination,
|
||||||
|
Result* left_side,
|
||||||
|
Result* right_side,
|
||||||
|
bool left_side_constant_smi,
|
||||||
|
bool right_side_constant_smi,
|
||||||
|
bool is_loop_condition);
|
||||||
|
|
||||||
void GenerateInlineNumberComparison(Result* left_side,
|
void GenerateInlineNumberComparison(Result* left_side,
|
||||||
Result* right_side,
|
Result* right_side,
|
||||||
Condition cc,
|
Condition cc,
|
||||||
@ -578,6 +597,7 @@ class CodeGenerator: public AstVisitor {
|
|||||||
void GenerateIsArray(ZoneList<Expression*>* args);
|
void GenerateIsArray(ZoneList<Expression*>* args);
|
||||||
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
void GenerateIsRegExp(ZoneList<Expression*>* args);
|
||||||
void GenerateIsObject(ZoneList<Expression*>* args);
|
void GenerateIsObject(ZoneList<Expression*>* args);
|
||||||
|
void GenerateIsSpecObject(ZoneList<Expression*>* args);
|
||||||
void GenerateIsFunction(ZoneList<Expression*>* args);
|
void GenerateIsFunction(ZoneList<Expression*>* args);
|
||||||
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
19
deps/v8/src/x64/full-codegen-x64.cc
vendored
19
deps/v8/src/x64/full-codegen-x64.cc
vendored
@ -1991,6 +1991,25 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
|
||||||
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
VisitForValue(args->at(0), kAccumulator);
|
||||||
|
|
||||||
|
Label materialize_true, materialize_false;
|
||||||
|
Label* if_true = NULL;
|
||||||
|
Label* if_false = NULL;
|
||||||
|
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
|
||||||
|
|
||||||
|
__ JumpIfSmi(rax, if_false);
|
||||||
|
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
|
||||||
|
__ j(above_equal, if_true);
|
||||||
|
__ jmp(if_false);
|
||||||
|
|
||||||
|
Apply(context_, if_true, if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
|
||||||
ASSERT(args->length() == 1);
|
ASSERT(args->length() == 1);
|
||||||
|
|
||||||
|
36
deps/v8/src/x64/ic-x64.cc
vendored
36
deps/v8/src/x64/ic-x64.cc
vendored
@ -418,28 +418,6 @@ bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void KeyedLoadIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Insert null as the map to check for to make sure the map check fails
|
|
||||||
// sending control flow to the IC instead of the inlined version.
|
|
||||||
PatchInlinedLoad(address, Heap::null_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Insert null as the elements map to check for. This will make
|
|
||||||
// sure that the elements fast-case map check fails so that control
|
|
||||||
// flows to the IC instead of the inlined version.
|
|
||||||
PatchInlinedStore(address, Heap::null_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedStoreIC::RestoreInlinedVersion(Address address) {
|
|
||||||
// Restore the fast-case elements map check so that the inlined
|
|
||||||
// version can be used again.
|
|
||||||
PatchInlinedStore(address, Heap::fixed_array_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
// -- rax : key
|
// -- rax : key
|
||||||
@ -1630,14 +1608,6 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
|||||||
const int LoadIC::kOffsetToLoadInstruction = 20;
|
const int LoadIC::kOffsetToLoadInstruction = 20;
|
||||||
|
|
||||||
|
|
||||||
void LoadIC::ClearInlinedVersion(Address address) {
|
|
||||||
// Reset the map check of the inlined inobject property load (if
|
|
||||||
// present) to guarantee failure by holding an invalid map (the null
|
|
||||||
// value). The offset can be patched to anything.
|
|
||||||
PatchInlinedLoad(address, Heap::null_value(), kMaxInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
// -- rax : receiver
|
// -- rax : receiver
|
||||||
@ -1767,6 +1737,12 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
|
||||||
|
// TODO(787): Implement inline stores on x64.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
// -- rax : value
|
// -- rax : value
|
||||||
|
20
deps/v8/src/x64/virtual-frame-x64.cc
vendored
20
deps/v8/src/x64/virtual-frame-x64.cc
vendored
@ -1168,6 +1168,26 @@ Result VirtualFrame::CallCommonStoreIC(Handle<Code> ic,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
|
||||||
|
// Value and (if not contextual) receiver are on top of the frame.
|
||||||
|
// The IC expects name in rcx, value in rax, and receiver in rdx.
|
||||||
|
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
||||||
|
Result value = Pop();
|
||||||
|
if (is_contextual) {
|
||||||
|
PrepareForCall(0, 0);
|
||||||
|
value.ToRegister(rax);
|
||||||
|
__ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
|
||||||
|
value.Unuse();
|
||||||
|
} else {
|
||||||
|
Result receiver = Pop();
|
||||||
|
PrepareForCall(0, 0);
|
||||||
|
MoveResultsToRegisters(&value, &receiver, rax, rdx);
|
||||||
|
}
|
||||||
|
__ Move(rcx, name);
|
||||||
|
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
|
Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
|
||||||
int arg_count,
|
int arg_count,
|
||||||
int loop_nesting) {
|
int loop_nesting) {
|
||||||
|
6
deps/v8/src/x64/virtual-frame-x64.h
vendored
6
deps/v8/src/x64/virtual-frame-x64.h
vendored
@ -341,7 +341,7 @@ class VirtualFrame : public ZoneObject {
|
|||||||
// and by the order of the three arguments on the frame.
|
// and by the order of the three arguments on the frame.
|
||||||
Result CallCommonStoreIC(Handle<Code> ic,
|
Result CallCommonStoreIC(Handle<Code> ic,
|
||||||
Result* value,
|
Result* value,
|
||||||
Result *key,
|
Result* key,
|
||||||
Result* receiver);
|
Result* receiver);
|
||||||
|
|
||||||
// Call store IC. Name, value, and receiver are found on top
|
// Call store IC. Name, value, and receiver are found on top
|
||||||
@ -354,6 +354,10 @@ class VirtualFrame : public ZoneObject {
|
|||||||
return CallCommonStoreIC(ic, &value, &name, &receiver);
|
return CallCommonStoreIC(ic, &value, &name, &receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call store IC. If the load is contextual, value is found on top of the
|
||||||
|
// frame. If not, value and receiver are on the frame. Both are dropped.
|
||||||
|
Result CallStoreIC(Handle<String> name, bool is_contextual);
|
||||||
|
|
||||||
// Call keyed store IC. Value, key, and receiver are found on top
|
// Call keyed store IC. Value, key, and receiver are found on top
|
||||||
// of the frame. All are dropped.
|
// of the frame. All are dropped.
|
||||||
Result CallKeyedStoreIC() {
|
Result CallKeyedStoreIC() {
|
||||||
|
5
deps/v8/test/cctest/test-api.cc
vendored
5
deps/v8/test/cctest/test-api.cc
vendored
@ -8015,9 +8015,10 @@ TEST(DontLeakGlobalObjects) {
|
|||||||
v8::Persistent<v8::Object> some_object;
|
v8::Persistent<v8::Object> some_object;
|
||||||
v8::Persistent<v8::Object> bad_handle;
|
v8::Persistent<v8::Object> bad_handle;
|
||||||
|
|
||||||
void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
|
void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
bad_handle = v8::Persistent<v8::Object>::New(some_object);
|
bad_handle = v8::Persistent<v8::Object>::New(some_object);
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8046,6 +8047,7 @@ v8::Persistent<v8::Object> to_be_disposed;
|
|||||||
void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
|
void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
|
||||||
to_be_disposed.Dispose();
|
to_be_disposed.Dispose();
|
||||||
i::Heap::CollectAllGarbage(false);
|
i::Heap::CollectAllGarbage(false);
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8070,6 +8072,7 @@ void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
|
|||||||
void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
|
void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Persistent<v8::Object>::New(v8::Object::New());
|
v8::Persistent<v8::Object>::New(v8::Object::New());
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
34
deps/v8/test/cctest/test-assembler-arm.cc
vendored
34
deps/v8/test/cctest/test-assembler-arm.cc
vendored
@ -310,4 +310,38 @@ TEST(5) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(6) {
|
||||||
|
// Test saturating instructions.
|
||||||
|
InitializeVM();
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
Assembler assm(NULL, 0);
|
||||||
|
|
||||||
|
if (CpuFeatures::IsSupported(ARMv7)) {
|
||||||
|
CpuFeatures::Scope scope(ARMv7);
|
||||||
|
__ usat(r1, 8, Operand(r0)); // Sat 0xFFFF to 0-255 = 0xFF.
|
||||||
|
__ usat(r2, 12, Operand(r0, ASR, 9)); // Sat (0xFFFF>>9) to 0-4095 = 0x7F.
|
||||||
|
__ usat(r3, 1, Operand(r0, LSL, 16)); // Sat (0xFFFF<<16) to 0-1 = 0x0.
|
||||||
|
__ add(r0, r1, Operand(r2));
|
||||||
|
__ add(r0, r0, Operand(r3));
|
||||||
|
__ mov(pc, Operand(lr));
|
||||||
|
|
||||||
|
CodeDesc desc;
|
||||||
|
assm.GetCode(&desc);
|
||||||
|
Object* code = Heap::CreateCode(desc,
|
||||||
|
Code::ComputeFlags(Code::STUB),
|
||||||
|
Handle<Object>(Heap::undefined_value()));
|
||||||
|
CHECK(code->IsCode());
|
||||||
|
#ifdef DEBUG
|
||||||
|
Code::cast(code)->Print();
|
||||||
|
#endif
|
||||||
|
F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
|
||||||
|
int res = reinterpret_cast<int>(
|
||||||
|
CALL_GENERATED_CODE(f, 0xFFFF, 0, 0, 0, 0));
|
||||||
|
::printf("f() = %d\n", res);
|
||||||
|
CHECK_EQ(382, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#undef __
|
#undef __
|
||||||
|
9
deps/v8/test/cctest/test-disasm-arm.cc
vendored
9
deps/v8/test/cctest/test-disasm-arm.cc
vendored
@ -396,6 +396,15 @@ TEST(Type3) {
|
|||||||
"e7df0f91 bfi r0, r1, #31, #1");
|
"e7df0f91 bfi r0, r1, #31, #1");
|
||||||
COMPARE(bfi(r1, r0, 31, 1),
|
COMPARE(bfi(r1, r0, 31, 1),
|
||||||
"e7df1f90 bfi r1, r0, #31, #1");
|
"e7df1f90 bfi r1, r0, #31, #1");
|
||||||
|
|
||||||
|
COMPARE(usat(r0, 1, Operand(r1)),
|
||||||
|
"e6e10011 usat r0, #1, r1");
|
||||||
|
COMPARE(usat(r2, 7, Operand(lr)),
|
||||||
|
"e6e7201e usat r2, #7, lr");
|
||||||
|
COMPARE(usat(r3, 31, Operand(r4, LSL, 31)),
|
||||||
|
"e6ff3f94 usat r3, #31, r4, lsl #31");
|
||||||
|
COMPARE(usat(r8, 0, Operand(r5, ASR, 17)),
|
||||||
|
"e6e088d5 usat r8, #0, r5, asr #17");
|
||||||
}
|
}
|
||||||
|
|
||||||
VERIFY_RUN();
|
VERIFY_RUN();
|
||||||
|
173
deps/v8/test/cctest/test-heap-profiler.cc
vendored
173
deps/v8/test/cctest/test-heap-profiler.cc
vendored
@ -56,8 +56,7 @@ class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile {
|
|||||||
|
|
||||||
TEST(ConstructorProfile) {
|
TEST(ConstructorProfile) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
CompileAndRunScript(
|
CompileAndRunScript(
|
||||||
"function F() {} // A constructor\n"
|
"function F() {} // A constructor\n"
|
||||||
@ -144,8 +143,7 @@ static inline void CheckNonEqualsHelper(const char* file, int line,
|
|||||||
|
|
||||||
TEST(ClustersCoarserSimple) {
|
TEST(ClustersCoarserSimple) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
||||||
|
|
||||||
@ -183,8 +181,7 @@ TEST(ClustersCoarserSimple) {
|
|||||||
|
|
||||||
TEST(ClustersCoarserMultipleConstructors) {
|
TEST(ClustersCoarserMultipleConstructors) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
||||||
|
|
||||||
@ -214,8 +211,7 @@ TEST(ClustersCoarserMultipleConstructors) {
|
|||||||
|
|
||||||
TEST(ClustersCoarserPathsTraversal) {
|
TEST(ClustersCoarserPathsTraversal) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
||||||
|
|
||||||
@ -267,8 +263,7 @@ TEST(ClustersCoarserPathsTraversal) {
|
|||||||
|
|
||||||
TEST(ClustersCoarserSelf) {
|
TEST(ClustersCoarserSelf) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
|
||||||
|
|
||||||
@ -362,8 +357,7 @@ class RetainerProfilePrinter : public RetainerHeapProfile::Printer {
|
|||||||
|
|
||||||
TEST(RetainerProfile) {
|
TEST(RetainerProfile) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
CompileAndRunScript(
|
CompileAndRunScript(
|
||||||
"function A() {}\n"
|
"function A() {}\n"
|
||||||
@ -431,8 +425,8 @@ class NamedEntriesDetector {
|
|||||||
|
|
||||||
static const v8::HeapGraphNode* GetGlobalObject(
|
static const v8::HeapGraphNode* GetGlobalObject(
|
||||||
const v8::HeapSnapshot* snapshot) {
|
const v8::HeapSnapshot* snapshot) {
|
||||||
CHECK_EQ(1, snapshot->GetHead()->GetChildrenCount());
|
CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount());
|
||||||
return snapshot->GetHead()->GetChild(0)->GetToNode();
|
return snapshot->GetRoot()->GetChild(0)->GetToNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -449,6 +443,19 @@ static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
|
||||||
|
v8::HeapGraphEdge::Type type,
|
||||||
|
const char* name) {
|
||||||
|
for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
|
||||||
|
const v8::HeapGraphEdge* prop = node->GetRetainer(i);
|
||||||
|
v8::String::AsciiValue prop_name(prop->GetName());
|
||||||
|
if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
|
static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
|
||||||
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
||||||
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
||||||
@ -464,11 +471,9 @@ static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
|
|||||||
|
|
||||||
TEST(HeapSnapshot) {
|
TEST(HeapSnapshot) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
|
|
||||||
v8::Handle<v8::String> token1 = v8::String::New("token1");
|
v8::Handle<v8::String> token1 = v8::String::New("token1");
|
||||||
v8::Handle<v8::Context> env1 = v8::Context::New();
|
LocalContext env1;
|
||||||
env1->SetSecurityToken(token1);
|
env1->SetSecurityToken(token1);
|
||||||
env1->Enter();
|
|
||||||
|
|
||||||
CompileAndRunScript(
|
CompileAndRunScript(
|
||||||
"function A1() {}\n"
|
"function A1() {}\n"
|
||||||
@ -479,9 +484,8 @@ TEST(HeapSnapshot) {
|
|||||||
"var c1 = new C1(a1);");
|
"var c1 = new C1(a1);");
|
||||||
|
|
||||||
v8::Handle<v8::String> token2 = v8::String::New("token2");
|
v8::Handle<v8::String> token2 = v8::String::New("token2");
|
||||||
v8::Handle<v8::Context> env2 = v8::Context::New();
|
LocalContext env2;
|
||||||
env2->SetSecurityToken(token2);
|
env2->SetSecurityToken(token2);
|
||||||
env2->Enter();
|
|
||||||
|
|
||||||
CompileAndRunScript(
|
CompileAndRunScript(
|
||||||
"function A2() {}\n"
|
"function A2() {}\n"
|
||||||
@ -569,8 +573,7 @@ TEST(HeapSnapshot) {
|
|||||||
|
|
||||||
TEST(HeapSnapshotCodeObjects) {
|
TEST(HeapSnapshotCodeObjects) {
|
||||||
v8::HandleScope scope;
|
v8::HandleScope scope;
|
||||||
v8::Handle<v8::Context> env = v8::Context::New();
|
LocalContext env;
|
||||||
env->Enter();
|
|
||||||
|
|
||||||
CompileAndRunScript(
|
CompileAndRunScript(
|
||||||
"function lazy(x) { return x - 1; }\n"
|
"function lazy(x) { return x - 1; }\n"
|
||||||
@ -625,4 +628,132 @@ TEST(HeapSnapshotCodeObjects) {
|
|||||||
CHECK(!lazy_references_x);
|
CHECK(!lazy_references_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Trying to introduce a check helper for uint64_t causes many
|
||||||
|
// overloading ambiguities, so it seems easier just to cast
|
||||||
|
// them to a signed type.
|
||||||
|
#define CHECK_EQ_UINT64_T(a, b) \
|
||||||
|
CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
|
||||||
|
#define CHECK_NE_UINT64_T(a, b) do \
|
||||||
|
{ \
|
||||||
|
bool ne = a != b; \
|
||||||
|
CHECK(ne); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
TEST(HeapEntryIdsAndGC) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
LocalContext env;
|
||||||
|
|
||||||
|
CompileAndRunScript(
|
||||||
|
"function A() {}\n"
|
||||||
|
"function B(x) { this.x = x; }\n"
|
||||||
|
"var a = new A();\n"
|
||||||
|
"var b = new B(a);");
|
||||||
|
const v8::HeapSnapshot* snapshot1 =
|
||||||
|
v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
|
||||||
|
|
||||||
|
i::Heap::CollectAllGarbage(true); // Enforce compaction.
|
||||||
|
|
||||||
|
const v8::HeapSnapshot* snapshot2 =
|
||||||
|
v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
|
||||||
|
|
||||||
|
const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
|
||||||
|
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
|
||||||
|
CHECK_NE_UINT64_T(0, global1->GetId());
|
||||||
|
CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
|
||||||
|
const v8::HeapGraphNode* A1 =
|
||||||
|
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "A");
|
||||||
|
const v8::HeapGraphNode* A2 =
|
||||||
|
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "A");
|
||||||
|
CHECK_NE_UINT64_T(0, A1->GetId());
|
||||||
|
CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
|
||||||
|
const v8::HeapGraphNode* B1 =
|
||||||
|
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "B");
|
||||||
|
const v8::HeapGraphNode* B2 =
|
||||||
|
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "B");
|
||||||
|
CHECK_NE_UINT64_T(0, B1->GetId());
|
||||||
|
CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
|
||||||
|
const v8::HeapGraphNode* a1 =
|
||||||
|
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "a");
|
||||||
|
const v8::HeapGraphNode* a2 =
|
||||||
|
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "a");
|
||||||
|
CHECK_NE_UINT64_T(0, a1->GetId());
|
||||||
|
CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
|
||||||
|
const v8::HeapGraphNode* b1 =
|
||||||
|
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "b");
|
||||||
|
const v8::HeapGraphNode* b2 =
|
||||||
|
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "b");
|
||||||
|
CHECK_NE_UINT64_T(0, b1->GetId());
|
||||||
|
CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(HeapSnapshotsDiff) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
LocalContext env;
|
||||||
|
|
||||||
|
CompileAndRunScript(
|
||||||
|
"function A() {}\n"
|
||||||
|
"function B(x) { this.x = x; }\n"
|
||||||
|
"var a = new A();\n"
|
||||||
|
"var b = new B(a);");
|
||||||
|
const v8::HeapSnapshot* snapshot1 =
|
||||||
|
v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
|
||||||
|
|
||||||
|
CompileAndRunScript(
|
||||||
|
"delete a;\n"
|
||||||
|
"b.x = null;\n"
|
||||||
|
"var a = new A();\n"
|
||||||
|
"var b2 = new B(a);");
|
||||||
|
const v8::HeapSnapshot* snapshot2 =
|
||||||
|
v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
|
||||||
|
|
||||||
|
const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2);
|
||||||
|
|
||||||
|
// Verify additions: ensure that addition of A and B was detected.
|
||||||
|
const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot();
|
||||||
|
bool found_A = false, found_B = false;
|
||||||
|
uint64_t s1_A_id = 0;
|
||||||
|
for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) {
|
||||||
|
const v8::HeapGraphEdge* prop = additions_root->GetChild(i);
|
||||||
|
const v8::HeapGraphNode* node = prop->GetToNode();
|
||||||
|
if (node->GetType() == v8::HeapGraphNode::OBJECT) {
|
||||||
|
v8::String::AsciiValue node_name(node->GetName());
|
||||||
|
if (strcmp(*node_name, "A") == 0) {
|
||||||
|
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a"));
|
||||||
|
CHECK(!found_A);
|
||||||
|
found_A = true;
|
||||||
|
s1_A_id = node->GetId();
|
||||||
|
} else if (strcmp(*node_name, "B") == 0) {
|
||||||
|
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "b2"));
|
||||||
|
CHECK(!found_B);
|
||||||
|
found_B = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(found_A);
|
||||||
|
CHECK(found_B);
|
||||||
|
|
||||||
|
// Verify deletions: ensure that deletion of A was detected.
|
||||||
|
const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot();
|
||||||
|
bool found_A_del = false;
|
||||||
|
uint64_t s2_A_id = 0;
|
||||||
|
for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) {
|
||||||
|
const v8::HeapGraphEdge* prop = deletions_root->GetChild(i);
|
||||||
|
const v8::HeapGraphNode* node = prop->GetToNode();
|
||||||
|
if (node->GetType() == v8::HeapGraphNode::OBJECT) {
|
||||||
|
v8::String::AsciiValue node_name(node->GetName());
|
||||||
|
if (strcmp(*node_name, "A") == 0) {
|
||||||
|
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a"));
|
||||||
|
CHECK(!found_A_del);
|
||||||
|
found_A_del = true;
|
||||||
|
s2_A_id = node->GetId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(found_A_del);
|
||||||
|
CHECK_NE_UINT64_T(0, s1_A_id);
|
||||||
|
CHECK(s1_A_id != s2_A_id);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ENABLE_LOGGING_AND_PROFILING
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||||
|
13
deps/v8/test/cctest/test-heap.cc
vendored
13
deps/v8/test/cctest/test-heap.cc
vendored
@ -322,8 +322,8 @@ static bool WeakPointerCleared = false;
|
|||||||
|
|
||||||
static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
|
static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
|
||||||
void* id) {
|
void* id) {
|
||||||
USE(handle);
|
|
||||||
if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
|
if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -398,17 +398,8 @@ TEST(WeakGlobalHandlesMark) {
|
|||||||
|
|
||||||
CHECK(WeakPointerCleared);
|
CHECK(WeakPointerCleared);
|
||||||
CHECK(!GlobalHandles::IsNearDeath(h1.location()));
|
CHECK(!GlobalHandles::IsNearDeath(h1.location()));
|
||||||
CHECK(GlobalHandles::IsNearDeath(h2.location()));
|
|
||||||
|
|
||||||
GlobalHandles::Destroy(h1.location());
|
GlobalHandles::Destroy(h1.location());
|
||||||
GlobalHandles::Destroy(h2.location());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void TestDeleteWeakGlobalHandleCallback(
|
|
||||||
v8::Persistent<v8::Value> handle,
|
|
||||||
void* id) {
|
|
||||||
if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true;
|
|
||||||
handle.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DeleteWeakGlobalHandle) {
|
TEST(DeleteWeakGlobalHandle) {
|
||||||
@ -427,7 +418,7 @@ TEST(DeleteWeakGlobalHandle) {
|
|||||||
|
|
||||||
GlobalHandles::MakeWeak(h.location(),
|
GlobalHandles::MakeWeak(h.location(),
|
||||||
reinterpret_cast<void*>(1234),
|
reinterpret_cast<void*>(1234),
|
||||||
&TestDeleteWeakGlobalHandleCallback);
|
&TestWeakGlobalHandleCallback);
|
||||||
|
|
||||||
// Scanvenge does not recognize weak reference.
|
// Scanvenge does not recognize weak reference.
|
||||||
Heap::PerformScavenge();
|
Heap::PerformScavenge();
|
||||||
|
1
deps/v8/test/cctest/test-mark-compact.cc
vendored
1
deps/v8/test/cctest/test-mark-compact.cc
vendored
@ -273,6 +273,7 @@ TEST(GCCallback) {
|
|||||||
static int NumberOfWeakCalls = 0;
|
static int NumberOfWeakCalls = 0;
|
||||||
static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
|
static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
|
||||||
NumberOfWeakCalls++;
|
NumberOfWeakCalls++;
|
||||||
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ObjectGroups) {
|
TEST(ObjectGroups) {
|
||||||
|
57
deps/v8/test/mjsunit/for-in-special-cases.js
vendored
57
deps/v8/test/mjsunit/for-in-special-cases.js
vendored
@ -62,3 +62,60 @@ for (var j = 0; j < 10; ++j) {
|
|||||||
assertEquals(10, i);
|
assertEquals(10, i);
|
||||||
assertEquals(10, j);
|
assertEquals(10, j);
|
||||||
|
|
||||||
|
|
||||||
|
function Accumulate(x) {
|
||||||
|
var accumulator = "";
|
||||||
|
for (var i in x) {
|
||||||
|
accumulator += i;
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
var elements = Accumulate("abcd");
|
||||||
|
// We do not assume that for-in enumerates elements in order.
|
||||||
|
assertTrue(-1 != elements.indexOf("0"));
|
||||||
|
assertTrue(-1 != elements.indexOf("1"));
|
||||||
|
assertTrue(-1 != elements.indexOf("2"));
|
||||||
|
assertTrue(-1 != elements.indexOf("3"));
|
||||||
|
assertEquals(4, elements.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function for_in_string_prototype() {
|
||||||
|
|
||||||
|
var x = new String("abc");
|
||||||
|
x.foo = 19;
|
||||||
|
function B() {
|
||||||
|
this.bar = 5;
|
||||||
|
this[7] = 4;
|
||||||
|
}
|
||||||
|
B.prototype = x;
|
||||||
|
|
||||||
|
var y = new B();
|
||||||
|
y.gub = 13;
|
||||||
|
|
||||||
|
var elements = Accumulate(y);
|
||||||
|
var elements1 = Accumulate(y);
|
||||||
|
// If for-in returns elements in a different order on multiple calls, this
|
||||||
|
// assert will fail. If that happens, consider if that behavior is OK.
|
||||||
|
assertEquals(elements, elements1, "For-in elements not the same both times.");
|
||||||
|
// We do not assume that for-in enumerates elements in order.
|
||||||
|
assertTrue(-1 != elements.indexOf("0"));
|
||||||
|
assertTrue(-1 != elements.indexOf("1"));
|
||||||
|
assertTrue(-1 != elements.indexOf("2"));
|
||||||
|
assertTrue(-1 != elements.indexOf("7"));
|
||||||
|
assertTrue(-1 != elements.indexOf("foo"));
|
||||||
|
assertTrue(-1 != elements.indexOf("bar"));
|
||||||
|
assertTrue(-1 != elements.indexOf("gub"));
|
||||||
|
assertEquals(13, elements.length);
|
||||||
|
|
||||||
|
elements = Accumulate(x);
|
||||||
|
assertTrue(-1 != elements.indexOf("0"));
|
||||||
|
assertTrue(-1 != elements.indexOf("1"));
|
||||||
|
assertTrue(-1 != elements.indexOf("2"));
|
||||||
|
assertTrue(-1 != elements.indexOf("foo"));
|
||||||
|
assertEquals(6, elements.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_in_string_prototype();
|
||||||
|
for_in_string_prototype();
|
||||||
|
49
deps/v8/tools/gc-nvp-trace-processor.py
vendored
Normal file → Executable file
49
deps/v8/tools/gc-nvp-trace-processor.py
vendored
Normal file → Executable file
@ -47,8 +47,12 @@ def flatten(l):
|
|||||||
|
|
||||||
def split_nvp(s):
|
def split_nvp(s):
|
||||||
t = {}
|
t = {}
|
||||||
for m in re.finditer(r"(\w+)=(-?\d+)", s):
|
for (name, value) in re.findall(r"(\w+)=([-\w]+)", s):
|
||||||
t[m.group(1)] = int(m.group(2))
|
try:
|
||||||
|
t[name] = int(value)
|
||||||
|
except ValueError:
|
||||||
|
t[name] = value
|
||||||
|
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def parse_gc_trace(input):
|
def parse_gc_trace(input):
|
||||||
@ -211,6 +215,9 @@ def plot_all(plots, trace, prefix):
|
|||||||
def reclaimed_bytes(row):
|
def reclaimed_bytes(row):
|
||||||
return row['total_size_before'] - row['total_size_after']
|
return row['total_size_before'] - row['total_size_after']
|
||||||
|
|
||||||
|
def other_scope(r):
|
||||||
|
return r['pause'] - r['mark'] - r['sweep'] - r['compact'] - r['flushcode']
|
||||||
|
|
||||||
plots = [
|
plots = [
|
||||||
[
|
[
|
||||||
Set('style fill solid 0.5 noborder'),
|
Set('style fill solid 0.5 noborder'),
|
||||||
@ -219,9 +226,8 @@ plots = [
|
|||||||
Plot(Item('Marking', 'mark', lc = 'purple'),
|
Plot(Item('Marking', 'mark', lc = 'purple'),
|
||||||
Item('Sweep', 'sweep', lc = 'blue'),
|
Item('Sweep', 'sweep', lc = 'blue'),
|
||||||
Item('Compaction', 'compact', lc = 'red'),
|
Item('Compaction', 'compact', lc = 'red'),
|
||||||
Item('Other',
|
Item('Flush Code', 'flushcode', lc = 'yellow'),
|
||||||
lambda r: r['pause'] - r['mark'] - r['sweep'] - r['compact'],
|
Item('Other', other_scope, lc = 'grey'))
|
||||||
lc = 'grey'))
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Set('style histogram rowstacked'),
|
Set('style histogram rowstacked'),
|
||||||
@ -256,19 +262,48 @@ plots = [
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def calc_total(trace, field):
|
||||||
|
return reduce(lambda t,r: t + r[field], trace, 0)
|
||||||
|
|
||||||
|
def calc_max(trace, field):
|
||||||
|
return reduce(lambda t,r: max(t, r[field]), trace, 0)
|
||||||
|
|
||||||
def process_trace(filename):
|
def process_trace(filename):
|
||||||
trace = parse_gc_trace(filename)
|
trace = parse_gc_trace(filename)
|
||||||
total_gc = reduce(lambda t,r: t + r['pause'], trace, 0)
|
total_gc = calc_total(trace, 'pause')
|
||||||
max_gc = reduce(lambda t,r: max(t, r['pause']), trace, 0)
|
max_gc = calc_max(trace, 'pause')
|
||||||
avg_gc = total_gc / len(trace)
|
avg_gc = total_gc / len(trace)
|
||||||
|
|
||||||
|
total_sweep = calc_total(trace, 'sweep')
|
||||||
|
max_sweep = calc_max(trace, 'sweep')
|
||||||
|
|
||||||
|
total_mark = calc_total(trace, 'mark')
|
||||||
|
max_mark = calc_max(trace, 'mark')
|
||||||
|
|
||||||
|
scavenges = filter(lambda r: r['gc'] == 's', trace)
|
||||||
|
total_scavenge = calc_total(scavenges, 'pause')
|
||||||
|
max_scavenge = calc_max(scavenges, 'pause')
|
||||||
|
avg_scavenge = total_scavenge / len(scavenges)
|
||||||
|
|
||||||
charts = plot_all(plots, trace, filename)
|
charts = plot_all(plots, trace, filename)
|
||||||
|
|
||||||
with open(filename + '.html', 'w') as out:
|
with open(filename + '.html', 'w') as out:
|
||||||
out.write('<html><body>')
|
out.write('<html><body>')
|
||||||
|
out.write('<table><tr><td>')
|
||||||
out.write('Total in GC: <b>%d</b><br/>' % total_gc)
|
out.write('Total in GC: <b>%d</b><br/>' % total_gc)
|
||||||
out.write('Max in GC: <b>%d</b><br/>' % max_gc)
|
out.write('Max in GC: <b>%d</b><br/>' % max_gc)
|
||||||
out.write('Avg in GC: <b>%d</b><br/>' % avg_gc)
|
out.write('Avg in GC: <b>%d</b><br/>' % avg_gc)
|
||||||
|
out.write('</td><td>')
|
||||||
|
out.write('Total in Scavenge: <b>%d</b><br/>' % total_scavenge)
|
||||||
|
out.write('Max in Scavenge: <b>%d</b><br/>' % max_scavenge)
|
||||||
|
out.write('Avg in Scavenge: <b>%d</b><br/>' % avg_scavenge)
|
||||||
|
out.write('</td><td>')
|
||||||
|
out.write('Total in Sweep: <b>%d</b><br/>' % total_sweep)
|
||||||
|
out.write('Max in Sweep: <b>%d</b><br/>' % max_sweep)
|
||||||
|
out.write('</td><td>')
|
||||||
|
out.write('Total in Mark: <b>%d</b><br/>' % total_mark)
|
||||||
|
out.write('Max in Mark: <b>%d</b><br/>' % max_mark)
|
||||||
|
out.write('</td></tr></table>')
|
||||||
for chart in charts:
|
for chart in charts:
|
||||||
out.write('<img src="%s">' % chart)
|
out.write('<img src="%s">' % chart)
|
||||||
out.write('</body></html>')
|
out.write('</body></html>')
|
||||||
|
10
deps/v8/tools/gyp/v8.gyp
vendored
10
deps/v8/tools/gyp/v8.gyp
vendored
@ -181,6 +181,11 @@
|
|||||||
'defines': [
|
'defines': [
|
||||||
'BUILDING_V8_SHARED'
|
'BUILDING_V8_SHARED'
|
||||||
],
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'defines': [
|
||||||
|
'USING_V8_SHARED',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type': 'none',
|
'type': 'none',
|
||||||
@ -738,11 +743,6 @@
|
|||||||
# This could be gotten by not setting chromium_code, if that's OK.
|
# This could be gotten by not setting chromium_code, if that's OK.
|
||||||
'defines': ['_CRT_SECURE_NO_WARNINGS'],
|
'defines': ['_CRT_SECURE_NO_WARNINGS'],
|
||||||
}],
|
}],
|
||||||
['OS=="win" and component=="shared_library"', {
|
|
||||||
'defines': [
|
|
||||||
'USING_V8_SHARED',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user