Improve positions recording for calls.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5768 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2010-11-04 15:12:03 +00:00
parent 455cfe5932
commit 746d72420c
20 changed files with 493 additions and 369 deletions

View File

@ -317,7 +317,8 @@ static const Instr kLdrStrOffsetMask = 0x00000fff;
static const int kMinimalBufferSize = 4*KB;
static byte* spare_buffer_ = NULL;
Assembler::Assembler(void* buffer, int buffer_size) {
Assembler::Assembler(void* buffer, int buffer_size)
: positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@ -354,10 +355,6 @@ Assembler::Assembler(void* buffer, int buffer_size) {
no_const_pool_before_ = 0;
last_const_pool_end_ = 0;
last_bound_pos_ = 0;
current_statement_position_ = RelocInfo::kNoPosition;
current_position_ = RelocInfo::kNoPosition;
written_statement_position_ = current_statement_position_;
written_position_ = current_position_;
}
@ -999,7 +996,7 @@ void Assembler::bl(int branch_offset, Condition cond) {
void Assembler::blx(int branch_offset) { // v5 and above
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
ASSERT((branch_offset & 1) == 0);
int h = ((branch_offset & 2) >> 1)*B24;
int imm24 = branch_offset >> 2;
@ -1009,14 +1006,14 @@ void Assembler::blx(int branch_offset) { // v5 and above
void Assembler::blx(Register target, Condition cond) { // v5 and above
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
ASSERT(!target.is(pc));
emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | 3*B4 | target.code());
}
void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
ASSERT(!target.is(pc)); // use of pc is actually allowed, but discouraged
emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | B4 | target.code());
}
@ -1114,7 +1111,7 @@ void Assembler::orr(Register dst, Register src1, const Operand& src2,
void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) {
if (dst.is(pc)) {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
}
// Don't allow nop instructions in the form mov rn, rn to be generated using
// the mov instruction. They must be generated using nop(int)
@ -1359,7 +1356,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
// Load/Store instructions.
void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
if (dst.is(pc)) {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
}
addrmod2(cond | B26 | L, dst, src);
@ -2377,14 +2374,14 @@ void Assembler::BlockConstPoolFor(int instructions) {
// Debugging.
void Assembler::RecordJSReturn() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
CheckBuffer();
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
CheckBuffer();
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@ -2398,47 +2395,6 @@ void Assembler::RecordComment(const char* msg) {
}
void Assembler::RecordPosition(int pos) {
if (pos == RelocInfo::kNoPosition) return;
ASSERT(pos >= 0);
current_position_ = pos;
}
void Assembler::RecordStatementPosition(int pos) {
if (pos == RelocInfo::kNoPosition) return;
ASSERT(pos >= 0);
current_statement_position_ = pos;
}
bool Assembler::WriteRecordedPositions() {
bool written = false;
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
CheckBuffer();
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
written = true;
}
// Write the position if it is different from what was written last time and
// also different from the written statement position.
if (current_position_ != written_position_ &&
current_position_ != written_statement_position_) {
CheckBuffer();
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
written = true;
}
// Return whether something was written.
return written;
}
void Assembler::GrowBuffer() {
if (!own_buffer_) FATAL("external code buffer is too small");

View File

@ -1117,13 +1117,9 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
bool WriteRecordedPositions();
int pc_offset() const { return pc_ - buffer_; }
int current_position() const { return current_position_; }
int current_statement_position() const { return current_statement_position_; }
PositionsRecorder* positions_recorder() { return &positions_recorder_; }
bool can_peephole_optimize(int instructions) {
if (!FLAG_peephole_optimization) return false;
@ -1259,12 +1255,6 @@ class Assembler : public Malloced {
// The bound position, before this we cannot do instruction elimination.
int last_bound_pos_;
// source position information
int current_position_;
int current_statement_position_;
int written_position_;
int written_statement_position_;
// Code emission
inline void CheckBuffer();
void GrowBuffer();
@ -1290,8 +1280,21 @@ class Assembler : public Malloced {
friend class RelocInfo;
friend class CodePatcher;
friend class BlockConstPoolScope;
PositionsRecorder positions_recorder_;
friend class PositionsRecorder;
friend class EnsureSpace;
};
class EnsureSpace BASE_EMBEDDED {
public:
EnsureSpace(Assembler* assembler) {
assembler->CheckBuffer();
}
};
} } // namespace v8::internal
#endif // V8_ARM_ASSEMBLER_ARM_H_

View File

@ -1688,12 +1688,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
__ mov(r2, Operand(name));
}
__ mov(r2, Operand(name));
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
@ -1710,13 +1712,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
VisitForAccumulatorValue(key);
__ mov(r2, r0);
}
VisitForAccumulatorValue(key);
__ mov(r2, r0);
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
@ -1732,11 +1736,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
}
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -1756,41 +1762,46 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
VisitForStackValue(fun);
__ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
__ push(r2); // Reserved receiver slot.
// Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
// Push copy of the function - found below the arguments.
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ push(r1);
{ PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
VisitForStackValue(fun);
__ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
__ push(r2); // Reserved receiver slot.
// Push the arguments.
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
// Push copy of the function - found below the arguments.
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ push(r1);
} else {
__ push(r2);
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
__ push(r1);
} else {
__ push(r2);
}
// Push the receiver of the enclosing function and do runtime call.
__ ldr(r1,
MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize));
__ push(r1);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in r0 (function) and
// r1 (receiver). Touch up the stack with the right values.
__ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ str(r1, MemOperand(sp, arg_count * kPointerSize));
}
// Push the receiver of the enclosing function and do runtime call.
__ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize));
__ push(r1);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in r0 (function) and
// r1 (receiver). Touch up the stack with the right values.
__ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ str(r1, MemOperand(sp, arg_count * kPointerSize));
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -1807,12 +1818,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
}
__ bind(&slow);
// Call the runtime to find the function to call (returned in r0)
@ -1846,17 +1859,23 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
VisitForStackValue(prop->obj());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use keyed CallIC.
VisitForStackValue(prop->obj());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
if (prop->is_synthetic()) {
VisitForAccumulatorValue(prop->key());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForAccumulatorValue(prop->key());
}
// Record source code position for IC call.
SetSourcePosition(prop->position());
SetSourcePosition(prop->position(), FORCED_POSITION);
__ pop(r1); // We do not need to keep the receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@ -1879,7 +1898,10 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
VisitForStackValue(fun);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
// Load global receiver object.
__ ldr(r1, CodeGenerator::GlobalObject());
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));

View File

@ -129,7 +129,7 @@ void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode,
// address is loaded. The mov method will automatically record
// positions when pc is the target, since this is not the case here
// we have to do it explicitly.
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
mov(ip, Operand(target, rmode), LeaveCC, cond);
blx(ip, cond);

View File

@ -804,4 +804,53 @@ ExternalReference ExternalReference::debug_step_in_fp_address() {
}
#endif
void PositionsRecorder::RecordPosition(int pos,
PositionRecordingType recording_type) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_position_ = pos;
current_position_recording_type_ = recording_type;
}
void PositionsRecorder::RecordStatementPosition(int pos) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_statement_position_ = pos;
}
bool PositionsRecorder::WriteRecordedPositions() {
bool written = false;
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
EnsureSpace ensure_space(assembler_);
assembler_->RecordRelocInfo(RelocInfo::STATEMENT_POSITION,
current_statement_position_);
written_statement_position_ = current_statement_position_;
written = true;
}
// Write the position if it is different from what was written last time and
// also different from the written statement position or was forced.
if (current_position_ != written_position_ &&
(current_position_ != current_statement_position_ || !written) &&
(current_position_ != written_statement_position_
|| current_position_recording_type_ == FORCED_POSITION)) {
EnsureSpace ensure_space(assembler_);
assembler_->RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
written = true;
}
current_position_recording_type_ = NORMAL_POSITION;
// Return whether something was written.
return written;
}
} } // namespace v8::internal

View File

@ -584,6 +584,67 @@ class ExternalReference BASE_EMBEDDED {
};
// -----------------------------------------------------------------------------
// Position recording support
enum PositionRecordingType { FORCED_POSITION, NORMAL_POSITION };
class PositionsRecorder BASE_EMBEDDED {
public:
explicit PositionsRecorder(Assembler* assembler)
: assembler_(assembler),
current_position_(RelocInfo::kNoPosition),
current_position_recording_type_(NORMAL_POSITION),
written_position_(RelocInfo::kNoPosition),
current_statement_position_(RelocInfo::kNoPosition),
written_statement_position_(RelocInfo::kNoPosition) { }
// Set current position to pos. If recording_type is FORCED_POSITION then
// WriteRecordedPositions will write this position even if it is equal to
// statement position previously written for another pc.
void RecordPosition(int pos,
PositionRecordingType recording_type = NORMAL_POSITION);
// Set current statement position to pos.
void RecordStatementPosition(int pos);
// Write recorded positions to relocation information.
bool WriteRecordedPositions();
int current_position() const { return current_position_; }
int current_statement_position() const { return current_statement_position_; }
private:
Assembler* assembler_;
int current_position_;
PositionRecordingType current_position_recording_type_;
int written_position_;
int current_statement_position_;
int written_statement_position_;
};
class PreserveStatementPositionScope BASE_EMBEDDED {
public:
PreserveStatementPositionScope(PositionsRecorder* positions_recorder)
: positions_recorder_(positions_recorder),
statement_position_(positions_recorder->current_statement_position()) {}
~PreserveStatementPositionScope() {
if (statement_position_ != RelocInfo::kNoPosition) {
positions_recorder_->RecordStatementPosition(statement_position_);
}
}
private:
PositionsRecorder* positions_recorder_;
int statement_position_;
};
// -----------------------------------------------------------------------------
// Utility functions

View File

@ -70,9 +70,10 @@ void CodeGenerator::ProcessDeferred() {
DeferredCode* code = deferred_.RemoveLast();
ASSERT(masm_ == code->masm());
// Record position of deferred code stub.
masm_->RecordStatementPosition(code->statement_position());
masm_->positions_recorder()->RecordStatementPosition(
code->statement_position());
if (code->position() != RelocInfo::kNoPosition) {
masm_->RecordPosition(code->position());
masm_->positions_recorder()->RecordPosition(code->position());
}
// Generate the code.
Comment cmnt(masm_, code->comment());
@ -402,10 +403,10 @@ bool CodeGenerator::RecordPositions(MacroAssembler* masm,
int pos,
bool right_here) {
if (pos != RelocInfo::kNoPosition) {
masm->RecordStatementPosition(pos);
masm->RecordPosition(pos);
masm->positions_recorder()->RecordStatementPosition(pos);
masm->positions_recorder()->RecordPosition(pos);
if (right_here) {
return masm->WriteRecordedPositions();
return masm->positions_recorder()->WriteRecordedPositions();
}
}
return false;
@ -435,7 +436,7 @@ void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) {
void CodeGenerator::CodeForSourcePosition(int pos) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
masm()->RecordPosition(pos);
masm()->positions_recorder()->RecordPosition(pos);
}
}

View File

@ -563,9 +563,10 @@ void FullCodeGenerator::SetStatementPosition(int pos) {
}
void FullCodeGenerator::SetSourcePosition(int pos) {
void FullCodeGenerator::SetSourcePosition(
int pos, PositionRecordingType recording_type) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
masm_->RecordPosition(pos);
masm_->positions_recorder()->RecordPosition(pos, recording_type);
}
}

View File

@ -423,7 +423,9 @@ class FullCodeGenerator: public AstVisitor {
void SetStatementPosition(Statement* stmt);
void SetExpressionPosition(Expression* expr, int pos);
void SetStatementPosition(int pos);
void SetSourcePosition(int pos);
void SetSourcePosition(
int pos,
PositionRecordingType recording_type = NORMAL_POSITION);
// Non-local control flow support.
void EnterFinallyBlock();

View File

@ -298,7 +298,8 @@ static void InitCoverageLog();
// Spare buffer.
byte* Assembler::spare_buffer_ = NULL;
Assembler::Assembler(void* buffer, int buffer_size) {
Assembler::Assembler(void* buffer, int buffer_size)
: positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@ -339,10 +340,6 @@ Assembler::Assembler(void* buffer, int buffer_size) {
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
last_pc_ = NULL;
current_statement_position_ = RelocInfo::kNoPosition;
current_position_ = RelocInfo::kNoPosition;
written_statement_position_ = current_statement_position_;
written_position_ = current_position_;
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
#endif
@ -1581,7 +1578,7 @@ void Assembler::call(const Operand& adr) {
void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
last_pc_ = pc_;
ASSERT(RelocInfo::IsCodeTarget(rmode));
@ -2464,14 +2461,14 @@ void Assembler::Print() {
void Assembler::RecordJSReturn() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@ -2485,47 +2482,6 @@ void Assembler::RecordComment(const char* msg) {
}
void Assembler::RecordPosition(int pos) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_position_ = pos;
}
void Assembler::RecordStatementPosition(int pos) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_statement_position_ = pos;
}
bool Assembler::WriteRecordedPositions() {
bool written = false;
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
written = true;
}
// Write the position if it is different from what was written last time and
// also different from the written statement position.
if (current_position_ != written_position_ &&
current_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
written = true;
}
// Return whether something was written.
return written;
}
void Assembler::GrowBuffer() {
ASSERT(overflow());
if (!own_buffer_) FATAL("external code buffer is too small");

View File

@ -847,17 +847,11 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
bool WriteRecordedPositions();
// Writes a single word of data in the code stream.
// Used for inline tables, e.g., jump-tables.
void dd(uint32_t data, RelocInfo::Mode reloc_info);
int pc_offset() const { return pc_ - buffer_; }
int current_statement_position() const { return current_statement_position_; }
int current_position() const { return current_position_; }
// Check if there is less than kGap bytes available in the buffer.
// If this is the case, we need to grow the buffer before emitting
@ -869,6 +863,8 @@ class Assembler : public Malloced {
static bool IsNop(Address addr) { return *addr == 0x90; }
PositionsRecorder* positions_recorder() { return &positions_recorder_; }
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
@ -947,11 +943,9 @@ class Assembler : public Malloced {
// push-pop elimination
byte* last_pc_;
// source position information
int current_statement_position_;
int current_position_;
int written_statement_position_;
int written_position_;
PositionsRecorder positions_recorder_;
friend class PositionsRecorder;
};

View File

@ -3734,7 +3734,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
CodeForStatementPosition(node);
Load(node->expression());
Result return_value = frame_->Pop();
masm()->WriteRecordedPositions();
masm()->positions_recorder()->WriteRecordedPositions();
if (function_return_is_shadowed_) {
function_return_.Jump(&return_value);
} else {

View File

@ -1996,12 +1996,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
__ Set(ecx, Immediate(name));
}
__ Set(ecx, Immediate(name));
// Record source position of the IC call.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
EmitCallIC(ic, mode);
@ -2017,13 +2019,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
VisitForAccumulatorValue(key);
__ mov(ecx, eax);
}
VisitForAccumulatorValue(key);
__ mov(ecx, eax);
// Record source position of the IC call.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
arg_count, in_loop);
@ -2038,11 +2042,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
}
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -2062,37 +2068,38 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
VisitForStackValue(fun);
__ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
// Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
VisitForStackValue(fun);
__ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
// Push the arguments.
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
// Push copy of the function - found below the arguments.
__ push(Operand(esp, (arg_count + 1) * kPointerSize));
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(esp, arg_count * kPointerSize));
} else {
__ push(Immediate(Factory::undefined_value()));
}
// Push the receiver of the enclosing function and do runtime call.
__ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
__ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
__ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
}
// Push copy of the function - found below the arguments.
__ push(Operand(esp, (arg_count + 1) * kPointerSize));
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(esp, arg_count * kPointerSize));
} else {
__ push(Immediate(Factory::undefined_value()));
}
// Push the receiver of the enclosing function and do runtime call.
__ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
__ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
__ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -2108,12 +2115,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
}
__ bind(&slow);
// Call the runtime to find the function to call (returned in eax)
@ -2152,11 +2161,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use keyed EmitCallIC.
VisitForStackValue(prop->obj());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
if (prop->is_synthetic()) {
VisitForAccumulatorValue(prop->key());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForAccumulatorValue(prop->key());
}
// Record source code position for IC call.
SetSourcePosition(prop->position());
SetSourcePosition(prop->position(), FORCED_POSITION);
__ pop(edx); // We do not need to keep the receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@ -2181,7 +2194,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
VisitForStackValue(fun);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
// Load global receiver object.
__ mov(ebx, CodeGenerator::GlobalObject());
__ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));

View File

@ -414,8 +414,9 @@ void BreakTarget::Branch(Condition cc, Hint hint) {
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
statement_position_(masm_->current_statement_position()),
position_(masm_->current_position()),
statement_position_(masm_->positions_recorder()->
current_statement_position()),
position_(masm_->positions_recorder()->current_position()),
frame_state_(CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);

View File

@ -36,8 +36,9 @@ namespace internal {
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
statement_position_(masm_->current_statement_position()),
position_(masm_->current_position()),
statement_position_(masm_->positions_recorder()->
current_statement_position()),
position_(masm_->positions_recorder()->current_position()),
frame_state_(*CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);

View File

@ -296,7 +296,7 @@ static void InitCoverageLog();
byte* Assembler::spare_buffer_ = NULL;
Assembler::Assembler(void* buffer, int buffer_size)
: code_targets_(100) {
: code_targets_(100), positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@ -337,10 +337,7 @@ Assembler::Assembler(void* buffer, int buffer_size)
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
last_pc_ = NULL;
current_statement_position_ = RelocInfo::kNoPosition;
current_position_ = RelocInfo::kNoPosition;
written_statement_position_ = current_statement_position_;
written_position_ = current_position_;
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
#endif
@ -845,7 +842,7 @@ void Assembler::call(Label* L) {
void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// 1110 1000 #32-bit disp.
@ -2935,14 +2932,14 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
}
void Assembler::RecordJSReturn() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
WriteRecordedPositions();
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@ -2956,47 +2953,6 @@ void Assembler::RecordComment(const char* msg) {
}
void Assembler::RecordPosition(int pos) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_position_ = pos;
}
void Assembler::RecordStatementPosition(int pos) {
ASSERT(pos != RelocInfo::kNoPosition);
ASSERT(pos >= 0);
current_statement_position_ = pos;
}
bool Assembler::WriteRecordedPositions() {
bool written = false;
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
written = true;
}
// Write the position if it is different from what was written last time and
// also different from the written statement position.
if (current_position_ != written_position_ &&
current_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
written = true;
}
// Return whether something was written.
return written;
}
const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
1 << RelocInfo::INTERNAL_REFERENCE;

View File

@ -1174,13 +1174,9 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
bool WriteRecordedPositions();
int pc_offset() const { return static_cast<int>(pc_ - buffer_); }
int current_statement_position() const { return current_statement_position_; }
int current_position() const { return current_position_; }
PositionsRecorder* positions_recorder() { return &positions_recorder_; }
// Check if there is less than kGap bytes available in the buffer.
// If this is the case, we need to grow the buffer before emitting
@ -1404,11 +1400,8 @@ class Assembler : public Malloced {
// push-pop elimination
byte* last_pc_;
// source position information
int current_statement_position_;
int current_position_;
int written_statement_position_;
int written_position_;
PositionsRecorder positions_recorder_;
friend class PositionsRecorder;
};

View File

@ -2956,7 +2956,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
CodeForStatementPosition(node);
Load(node->expression());
Result return_value = frame_->Pop();
masm()->WriteRecordedPositions();
masm()->positions_recorder()->WriteRecordedPositions();
if (function_return_is_shadowed_) {
function_return_.Jump(&return_value);
} else {

View File

@ -1717,12 +1717,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
__ Move(rcx, name);
}
__ Move(rcx, name);
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
@ -1740,13 +1742,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
VisitForAccumulatorValue(key);
__ movq(rcx, rax);
}
VisitForAccumulatorValue(key);
__ movq(rcx, rax);
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
@ -1762,11 +1766,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
}
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -1787,37 +1793,39 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. The we call the resolved function using the given
// arguments.
VisitForStackValue(fun);
__ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot.
// Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
{ PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
// Push copy of the function - found below the arguments.
__ push(Operand(rsp, (arg_count + 1) * kPointerSize));
VisitForStackValue(fun);
__ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot.
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(rsp, arg_count * kPointerSize));
} else {
// Push the arguments.
for (int i = 0; i < arg_count; i++) {
VisitForStackValue(args->at(i));
}
// Push copy of the function - found below the arguments.
__ push(Operand(rsp, (arg_count + 1) * kPointerSize));
// Push copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ push(Operand(rsp, arg_count * kPointerSize));
} else {
__ PushRoot(Heap::kUndefinedValueRootIndex);
}
// Push the receiver of the enclosing function and do runtime call.
__ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize));
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in rax (function) and
// rdx (receiver). Touch up the stack with the right values.
__ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
__ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
}
// Push the receiver of the enclosing function and do runtime call.
__ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize));
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// The runtime call returns a pair of values in rax (function) and
// rdx (receiver). Touch up the stack with the right values.
__ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
__ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
// Record source position for debugger.
SetSourcePosition(expr->position());
SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@ -1834,35 +1842,37 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
// Generate code for loading from variables potentially shadowed
// by eval-introduced variables.
EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
NOT_INSIDE_TYPEOF,
&slow,
&done);
__ bind(&slow);
// Call the runtime to find the function to call (returned in rax)
// and the object holding it (returned in rdx).
__ push(context_register());
__ Push(var->name());
__ CallRuntime(Runtime::kLoadContextSlot, 2);
__ push(rax); // Function.
__ push(rdx); // Receiver.
__ bind(&slow);
// Call the runtime to find the function to call (returned in rax)
// and the object holding it (returned in rdx).
__ push(context_register());
__ Push(var->name());
__ CallRuntime(Runtime::kLoadContextSlot, 2);
__ push(rax); // Function.
__ push(rdx); // Receiver.
// If fast case code has been generated, emit code to push the
// function and receiver and have the slow path jump around this
// code.
if (done.is_linked()) {
NearLabel call;
__ jmp(&call);
__ bind(&done);
// Push function.
__ push(rax);
// Push global receiver.
__ movq(rbx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
__ bind(&call);
// If fast case code has been generated, emit code to push the
// function and receiver and have the slow path jump around this
// code.
if (done.is_linked()) {
NearLabel call;
__ jmp(&call);
__ bind(&done);
// Push function.
__ push(rax);
// Push global receiver.
__ movq(rbx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
__ bind(&call);
}
}
EmitCallWithStub(expr);
@ -1873,18 +1883,24 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
VisitForStackValue(prop->obj());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use KeyedCallIC.
VisitForStackValue(prop->obj());
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
if (prop->is_synthetic()) {
VisitForAccumulatorValue(prop->key());
__ movq(rdx, Operand(rsp, 0));
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForAccumulatorValue(prop->key());
__ movq(rdx, Operand(rsp, 0));
}
// Record source code position for IC call.
SetSourcePosition(prop->position());
SetSourcePosition(prop->position(), FORCED_POSITION);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
// Pop receiver.
@ -1909,7 +1925,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
VisitForStackValue(fun);
{ PreserveStatementPositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
// Load global receiver object.
__ movq(rbx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));

View File

@ -0,0 +1,95 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --always-full-compiler
var functionToCatch;
var lineNumber;
function catchLineNumber () {
var x = {};
Error.prepareStackTrace = function (error, stackTrace) {
stackTrace.some(function (frame) {
if (frame.getFunction() == functionToCatch) {
lineNumber = frame.getLineNumber();
return true;
}
return false;
});
return lineNumber;
};
Error.captureStackTrace(x);
return x.stack;
}
function log() {
catchLineNumber();
}
function foo() {}
function test1() {
log(foo() == foo()
? 'a'
: 'b');
}
function test2() {
var o = { foo: function () {}}
log(o.foo() == o.foo()
? 'a'
: 'b');
}
function test3() {
var o = { log: log, foo: function() { } };
o.log(o.foo() == o.foo()
? 'a'
: 'b');
}
function test(f, expectedLineNumber) {
functionToCatch = f;
f();
assertEquals(expectedLineNumber, lineNumber);
}
test(test1, 58);
test(test2, 65);
test(test3, 72);
eval(test1.toString() + "//@ sourceUrl=foo");
eval(test2.toString() + "//@ sourceUrl=foo");
eval(test3.toString() + "//@ sourceUrl=foo");
test(test1, 2);
test(test2, 3);
test(test3, 3);