2012-05-03 17:31:34 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
2014-04-29 06:42:26 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
2010-02-17 20:37:08 +00:00
|
|
|
|
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/v8.h"
|
2010-02-17 20:37:08 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/liveedit.h"
|
2010-10-01 14:10:47 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/code-stubs.h"
|
|
|
|
#include "src/compilation-cache.h"
|
|
|
|
#include "src/compiler.h"
|
|
|
|
#include "src/debug.h"
|
|
|
|
#include "src/deoptimizer.h"
|
|
|
|
#include "src/global-handles.h"
|
|
|
|
#include "src/messages.h"
|
|
|
|
#include "src/parser.h"
|
|
|
|
#include "src/scopeinfo.h"
|
|
|
|
#include "src/scopes.h"
|
|
|
|
#include "src/v8memory.h"
|
2010-02-17 20:37:08 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
2014-03-11 14:39:08 +00:00
|
|
|
void SetElementSloppy(Handle<JSObject> object,
|
|
|
|
uint32_t index,
|
|
|
|
Handle<Object> value) {
|
2011-03-08 11:14:25 +00:00
|
|
|
// Ignore return value from SetElement. It can only be a failure if there
|
|
|
|
// are element setters causing exceptions and the debugger context has none
|
|
|
|
// of these.
|
2014-04-08 07:04:13 +00:00
|
|
|
JSObject::SetElement(object, index, value, NONE, SLOPPY).Assert();
|
2011-03-08 11:14:25 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2010-04-19 16:08:26 +00:00
|
|
|
// A simple implementation of dynamic programming algorithm. It solves
|
2011-06-01 23:11:10 +00:00
|
|
|
// the problem of finding the difference of 2 arrays. It uses a table of results
|
|
|
|
// of subproblems. Each cell contains a number together with 2-bit flag
|
2010-04-19 16:08:26 +00:00
|
|
|
// that helps building the chunk list.
|
|
|
|
class Differencer {
|
|
|
|
public:
|
2011-06-01 23:11:10 +00:00
|
|
|
explicit Differencer(Comparator::Input* input)
|
|
|
|
: input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
|
|
|
|
buffer_ = NewArray<int>(len1_ * len2_);
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
|
|
|
~Differencer() {
|
2011-06-01 23:11:10 +00:00
|
|
|
DeleteArray(buffer_);
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Initialize() {
|
|
|
|
int array_size = len1_ * len2_;
|
|
|
|
for (int i = 0; i < array_size; i++) {
|
|
|
|
buffer_[i] = kEmptyCellValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Makes sure that result for the full problem is calculated and stored
|
|
|
|
// in the table together with flags showing a path through subproblems.
|
|
|
|
void FillTable() {
|
|
|
|
CompareUpToTail(0, 0);
|
|
|
|
}
|
|
|
|
|
2010-04-27 21:20:02 +00:00
|
|
|
void SaveResult(Comparator::Output* chunk_writer) {
|
2010-04-19 16:08:26 +00:00
|
|
|
ResultWriter writer(chunk_writer);
|
|
|
|
|
|
|
|
int pos1 = 0;
|
|
|
|
int pos2 = 0;
|
|
|
|
while (true) {
|
|
|
|
if (pos1 < len1_) {
|
|
|
|
if (pos2 < len2_) {
|
|
|
|
Direction dir = get_direction(pos1, pos2);
|
|
|
|
switch (dir) {
|
|
|
|
case EQ:
|
|
|
|
writer.eq();
|
|
|
|
pos1++;
|
|
|
|
pos2++;
|
|
|
|
break;
|
|
|
|
case SKIP1:
|
|
|
|
writer.skip1(1);
|
|
|
|
pos1++;
|
|
|
|
break;
|
|
|
|
case SKIP2:
|
|
|
|
case SKIP_ANY:
|
|
|
|
writer.skip2(1);
|
|
|
|
pos2++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
writer.skip1(len1_ - pos1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (len2_ != pos2) {
|
|
|
|
writer.skip2(len2_ - pos2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
writer.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-04-27 21:20:02 +00:00
|
|
|
Comparator::Input* input_;
|
2010-04-19 16:08:26 +00:00
|
|
|
int* buffer_;
|
|
|
|
int len1_;
|
|
|
|
int len2_;
|
|
|
|
|
|
|
|
enum Direction {
|
|
|
|
EQ = 0,
|
|
|
|
SKIP1,
|
|
|
|
SKIP2,
|
|
|
|
SKIP_ANY,
|
|
|
|
|
|
|
|
MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
|
|
|
|
};
|
|
|
|
|
|
|
|
// Computes result for a subtask and optionally caches it in the buffer table.
|
|
|
|
// All results values are shifted to make space for flags in the lower bits.
|
|
|
|
int CompareUpToTail(int pos1, int pos2) {
|
|
|
|
if (pos1 < len1_) {
|
|
|
|
if (pos2 < len2_) {
|
|
|
|
int cached_res = get_value4(pos1, pos2);
|
|
|
|
if (cached_res == kEmptyCellValue) {
|
|
|
|
Direction dir;
|
|
|
|
int res;
|
2011-05-31 20:58:21 +00:00
|
|
|
if (input_->Equals(pos1, pos2)) {
|
2010-04-19 16:08:26 +00:00
|
|
|
res = CompareUpToTail(pos1 + 1, pos2 + 1);
|
|
|
|
dir = EQ;
|
|
|
|
} else {
|
|
|
|
int res1 = CompareUpToTail(pos1 + 1, pos2) +
|
|
|
|
(1 << kDirectionSizeBits);
|
|
|
|
int res2 = CompareUpToTail(pos1, pos2 + 1) +
|
|
|
|
(1 << kDirectionSizeBits);
|
|
|
|
if (res1 == res2) {
|
|
|
|
res = res1;
|
|
|
|
dir = SKIP_ANY;
|
|
|
|
} else if (res1 < res2) {
|
|
|
|
res = res1;
|
|
|
|
dir = SKIP1;
|
|
|
|
} else {
|
|
|
|
res = res2;
|
|
|
|
dir = SKIP2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
set_value4_and_dir(pos1, pos2, res, dir);
|
|
|
|
cached_res = res;
|
|
|
|
}
|
|
|
|
return cached_res;
|
|
|
|
} else {
|
|
|
|
return (len1_ - pos1) << kDirectionSizeBits;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return (len2_ - pos2) << kDirectionSizeBits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int& get_cell(int i1, int i2) {
|
|
|
|
return buffer_[i1 + i2 * len1_];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Each cell keeps a value plus direction. Value is multiplied by 4.
|
|
|
|
void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK((value4 & kDirectionMask) == 0);
|
2010-04-19 16:08:26 +00:00
|
|
|
get_cell(i1, i2) = value4 | dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_value4(int i1, int i2) {
|
|
|
|
return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
|
|
|
|
}
|
|
|
|
Direction get_direction(int i1, int i2) {
|
|
|
|
return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int kDirectionSizeBits = 2;
|
|
|
|
static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
|
2014-08-05 14:19:22 +00:00
|
|
|
static const int kEmptyCellValue = ~0u << kDirectionSizeBits;
|
2010-04-19 16:08:26 +00:00
|
|
|
|
|
|
|
// This method only holds static assert statement (unfortunately you cannot
|
|
|
|
// place one in class scope).
|
|
|
|
void StaticAssertHolder() {
|
|
|
|
STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
|
|
|
|
}
|
|
|
|
|
|
|
|
class ResultWriter {
|
|
|
|
public:
|
2010-04-27 21:20:02 +00:00
|
|
|
explicit ResultWriter(Comparator::Output* chunk_writer)
|
2010-04-19 16:08:26 +00:00
|
|
|
: chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
|
|
|
|
pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
|
|
|
|
}
|
|
|
|
void eq() {
|
|
|
|
FlushChunk();
|
|
|
|
pos1_++;
|
|
|
|
pos2_++;
|
|
|
|
}
|
|
|
|
void skip1(int len1) {
|
|
|
|
StartChunk();
|
|
|
|
pos1_ += len1;
|
|
|
|
}
|
|
|
|
void skip2(int len2) {
|
|
|
|
StartChunk();
|
|
|
|
pos2_ += len2;
|
|
|
|
}
|
|
|
|
void close() {
|
|
|
|
FlushChunk();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-04-27 21:20:02 +00:00
|
|
|
Comparator::Output* chunk_writer_;
|
2010-04-19 16:08:26 +00:00
|
|
|
int pos1_;
|
|
|
|
int pos2_;
|
|
|
|
int pos1_begin_;
|
|
|
|
int pos2_begin_;
|
|
|
|
bool has_open_chunk_;
|
|
|
|
|
|
|
|
void StartChunk() {
|
|
|
|
if (!has_open_chunk_) {
|
|
|
|
pos1_begin_ = pos1_;
|
|
|
|
pos2_begin_ = pos2_;
|
|
|
|
has_open_chunk_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlushChunk() {
|
|
|
|
if (has_open_chunk_) {
|
|
|
|
chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
|
|
|
|
pos1_ - pos1_begin_, pos2_ - pos2_begin_);
|
|
|
|
has_open_chunk_ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-06-01 23:11:10 +00:00
|
|
|
void Comparator::CalculateDifference(Comparator::Input* input,
|
|
|
|
Comparator::Output* result_writer) {
|
|
|
|
Differencer differencer(input);
|
2010-04-19 16:08:26 +00:00
|
|
|
differencer.Initialize();
|
|
|
|
differencer.FillTable();
|
|
|
|
differencer.SaveResult(result_writer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-28 20:24:47 +00:00
|
|
|
static bool CompareSubstrings(Handle<String> s1, int pos1,
|
2010-04-19 16:08:26 +00:00
|
|
|
Handle<String> s2, int pos2, int len) {
|
|
|
|
for (int i = 0; i < len; i++) {
|
2011-04-28 20:24:47 +00:00
|
|
|
if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
|
2010-04-19 16:08:26 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-31 20:58:21 +00:00
|
|
|
// Additional to Input interface. Lets switch Input range to subrange.
|
|
|
|
// More elegant way would be to wrap one Input as another Input object
|
|
|
|
// and translate positions there, but that would cost us additional virtual
|
|
|
|
// call per comparison.
|
|
|
|
class SubrangableInput : public Comparator::Input {
|
|
|
|
public:
|
|
|
|
virtual void SetSubrange1(int offset, int len) = 0;
|
|
|
|
virtual void SetSubrange2(int offset, int len) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class SubrangableOutput : public Comparator::Output {
|
|
|
|
public:
|
|
|
|
virtual void SetSubrange1(int offset, int len) = 0;
|
|
|
|
virtual void SetSubrange2(int offset, int len) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int min(int a, int b) {
|
|
|
|
return a < b ? a : b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Finds common prefix and suffix in input. This parts shouldn't take space in
|
|
|
|
// linear programming table. Enable subranging in input and output.
|
|
|
|
static void NarrowDownInput(SubrangableInput* input,
|
|
|
|
SubrangableOutput* output) {
|
|
|
|
const int len1 = input->GetLength1();
|
|
|
|
const int len2 = input->GetLength2();
|
|
|
|
|
|
|
|
int common_prefix_len;
|
|
|
|
int common_suffix_len;
|
|
|
|
|
|
|
|
{
|
|
|
|
common_prefix_len = 0;
|
|
|
|
int prefix_limit = min(len1, len2);
|
|
|
|
while (common_prefix_len < prefix_limit &&
|
|
|
|
input->Equals(common_prefix_len, common_prefix_len)) {
|
|
|
|
common_prefix_len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
common_suffix_len = 0;
|
|
|
|
int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
|
|
|
|
|
|
|
|
while (common_suffix_len < suffix_limit &&
|
|
|
|
input->Equals(len1 - common_suffix_len - 1,
|
|
|
|
len2 - common_suffix_len - 1)) {
|
|
|
|
common_suffix_len++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (common_prefix_len > 0 || common_suffix_len > 0) {
|
|
|
|
int new_len1 = len1 - common_suffix_len - common_prefix_len;
|
|
|
|
int new_len2 = len2 - common_suffix_len - common_prefix_len;
|
|
|
|
|
|
|
|
input->SetSubrange1(common_prefix_len, new_len1);
|
|
|
|
input->SetSubrange2(common_prefix_len, new_len2);
|
|
|
|
|
|
|
|
output->SetSubrange1(common_prefix_len, new_len1);
|
|
|
|
output->SetSubrange2(common_prefix_len, new_len2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-11 14:55:47 +00:00
|
|
|
// A helper class that writes chunk numbers into JSArray.
|
|
|
|
// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
|
|
|
|
class CompareOutputArrayWriter {
|
|
|
|
public:
|
2013-02-25 14:46:09 +00:00
|
|
|
explicit CompareOutputArrayWriter(Isolate* isolate)
|
|
|
|
: array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
|
2011-01-11 14:55:47 +00:00
|
|
|
|
|
|
|
Handle<JSArray> GetResult() {
|
|
|
|
return array_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate = array_->GetIsolate();
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(array_,
|
|
|
|
current_size_,
|
|
|
|
Handle<Object>(Smi::FromInt(char_pos1), isolate));
|
|
|
|
SetElementSloppy(array_,
|
|
|
|
current_size_ + 1,
|
|
|
|
Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
|
|
|
|
isolate));
|
|
|
|
SetElementSloppy(array_,
|
|
|
|
current_size_ + 2,
|
|
|
|
Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
|
|
|
|
isolate));
|
2011-01-11 14:55:47 +00:00
|
|
|
current_size_ += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<JSArray> array_;
|
|
|
|
int current_size_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Represents 2 strings as 2 arrays of tokens.
|
|
|
|
// TODO(LiveEdit): Currently it's actually an array of charactres.
|
|
|
|
// Make array of tokens instead.
|
|
|
|
class TokensCompareInput : public Comparator::Input {
|
|
|
|
public:
|
|
|
|
TokensCompareInput(Handle<String> s1, int offset1, int len1,
|
|
|
|
Handle<String> s2, int offset2, int len2)
|
|
|
|
: s1_(s1), offset1_(offset1), len1_(len1),
|
|
|
|
s2_(s2), offset2_(offset2), len2_(len2) {
|
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
virtual int GetLength1() {
|
2011-01-11 14:55:47 +00:00
|
|
|
return len1_;
|
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
virtual int GetLength2() {
|
2011-01-11 14:55:47 +00:00
|
|
|
return len2_;
|
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
bool Equals(int index1, int index2) {
|
2011-01-11 14:55:47 +00:00
|
|
|
return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<String> s1_;
|
|
|
|
int offset1_;
|
|
|
|
int len1_;
|
|
|
|
Handle<String> s2_;
|
|
|
|
int offset2_;
|
|
|
|
int len2_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Stores compare result in JSArray. Converts substring positions
|
|
|
|
// to absolute positions.
|
|
|
|
class TokensCompareOutput : public Comparator::Output {
|
|
|
|
public:
|
|
|
|
TokensCompareOutput(CompareOutputArrayWriter* array_writer,
|
|
|
|
int offset1, int offset2)
|
|
|
|
: array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddChunk(int pos1, int pos2, int len1, int len2) {
|
|
|
|
array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
CompareOutputArrayWriter* array_writer_;
|
|
|
|
int offset1_;
|
|
|
|
int offset2_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-04-19 16:08:26 +00:00
|
|
|
// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
|
|
|
|
// never has terminating new line character.
|
|
|
|
class LineEndsWrapper {
|
|
|
|
public:
|
|
|
|
explicit LineEndsWrapper(Handle<String> string)
|
2014-04-16 13:28:11 +00:00
|
|
|
: ends_array_(String::CalculateLineEnds(string, false)),
|
2010-04-19 16:08:26 +00:00
|
|
|
string_len_(string->length()) {
|
|
|
|
}
|
|
|
|
int length() {
|
|
|
|
return ends_array_->length() + 1;
|
|
|
|
}
|
|
|
|
// Returns start for any line including start of the imaginary line after
|
|
|
|
// the last line.
|
|
|
|
int GetLineStart(int index) {
|
|
|
|
if (index == 0) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return GetLineEnd(index - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int GetLineEnd(int index) {
|
|
|
|
if (index == ends_array_->length()) {
|
|
|
|
// End of the last line is always an end of the whole string.
|
|
|
|
// If the string ends with a new line character, the last line is an
|
|
|
|
// empty string after this character.
|
|
|
|
return string_len_;
|
|
|
|
} else {
|
|
|
|
return GetPosAfterNewLine(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<FixedArray> ends_array_;
|
|
|
|
int string_len_;
|
|
|
|
|
|
|
|
int GetPosAfterNewLine(int index) {
|
|
|
|
return Smi::cast(ends_array_->get(index))->value() + 1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Represents 2 strings as 2 arrays of lines.
|
2011-05-31 20:58:21 +00:00
|
|
|
class LineArrayCompareInput : public SubrangableInput {
|
2010-04-19 16:08:26 +00:00
|
|
|
public:
|
2011-04-28 20:24:47 +00:00
|
|
|
LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
|
2010-04-19 16:08:26 +00:00
|
|
|
LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
|
2011-04-28 20:24:47 +00:00
|
|
|
: s1_(s1), s2_(s2), line_ends1_(line_ends1),
|
2011-05-31 20:58:21 +00:00
|
|
|
line_ends2_(line_ends2),
|
|
|
|
subrange_offset1_(0), subrange_offset2_(0),
|
|
|
|
subrange_len1_(line_ends1_.length()),
|
|
|
|
subrange_len2_(line_ends2_.length()) {
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
int GetLength1() {
|
|
|
|
return subrange_len1_;
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
int GetLength2() {
|
|
|
|
return subrange_len2_;
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
bool Equals(int index1, int index2) {
|
|
|
|
index1 += subrange_offset1_;
|
|
|
|
index2 += subrange_offset2_;
|
|
|
|
|
2010-04-19 16:08:26 +00:00
|
|
|
int line_start1 = line_ends1_.GetLineStart(index1);
|
|
|
|
int line_start2 = line_ends2_.GetLineStart(index2);
|
|
|
|
int line_end1 = line_ends1_.GetLineEnd(index1);
|
|
|
|
int line_end2 = line_ends2_.GetLineEnd(index2);
|
|
|
|
int len1 = line_end1 - line_start1;
|
|
|
|
int len2 = line_end2 - line_start2;
|
|
|
|
if (len1 != len2) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-28 20:24:47 +00:00
|
|
|
return CompareSubstrings(s1_, line_start1, s2_, line_start2,
|
2011-03-18 20:35:07 +00:00
|
|
|
len1);
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
void SetSubrange1(int offset, int len) {
|
|
|
|
subrange_offset1_ = offset;
|
|
|
|
subrange_len1_ = len;
|
|
|
|
}
|
|
|
|
void SetSubrange2(int offset, int len) {
|
|
|
|
subrange_offset2_ = offset;
|
|
|
|
subrange_len2_ = len;
|
|
|
|
}
|
2010-04-19 16:08:26 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<String> s1_;
|
|
|
|
Handle<String> s2_;
|
|
|
|
LineEndsWrapper line_ends1_;
|
|
|
|
LineEndsWrapper line_ends2_;
|
2011-05-31 20:58:21 +00:00
|
|
|
int subrange_offset1_;
|
|
|
|
int subrange_offset2_;
|
|
|
|
int subrange_len1_;
|
|
|
|
int subrange_len2_;
|
2010-04-19 16:08:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-01-11 14:55:47 +00:00
|
|
|
// Stores compare result in JSArray. For each chunk tries to conduct
|
|
|
|
// a fine-grained nested diff token-wise.
|
2011-05-31 20:58:21 +00:00
|
|
|
class TokenizingLineArrayCompareOutput : public SubrangableOutput {
|
2010-04-19 16:08:26 +00:00
|
|
|
public:
|
2011-01-11 14:55:47 +00:00
|
|
|
TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
|
|
|
|
LineEndsWrapper line_ends2,
|
2011-06-01 23:11:10 +00:00
|
|
|
Handle<String> s1, Handle<String> s2)
|
2013-02-25 14:46:09 +00:00
|
|
|
: array_writer_(s1->GetIsolate()),
|
|
|
|
line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
|
2011-06-01 23:11:10 +00:00
|
|
|
subrange_offset1_(0), subrange_offset2_(0) {
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
|
2011-05-31 20:58:21 +00:00
|
|
|
line_pos1 += subrange_offset1_;
|
|
|
|
line_pos2 += subrange_offset2_;
|
|
|
|
|
2010-04-19 16:08:26 +00:00
|
|
|
int char_pos1 = line_ends1_.GetLineStart(line_pos1);
|
|
|
|
int char_pos2 = line_ends2_.GetLineStart(line_pos2);
|
|
|
|
int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
|
|
|
|
int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
|
|
|
|
|
2011-01-11 14:55:47 +00:00
|
|
|
if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
|
|
|
|
// Chunk is small enough to conduct a nested token-level diff.
|
2013-02-15 09:27:10 +00:00
|
|
|
HandleScope subTaskScope(s1_->GetIsolate());
|
2011-01-11 14:55:47 +00:00
|
|
|
|
|
|
|
TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
|
|
|
|
s2_, char_pos2, char_len2);
|
|
|
|
TokensCompareOutput tokens_output(&array_writer_, char_pos1,
|
|
|
|
char_pos2);
|
|
|
|
|
2011-06-01 23:11:10 +00:00
|
|
|
Comparator::CalculateDifference(&tokens_input, &tokens_output);
|
2011-01-11 14:55:47 +00:00
|
|
|
} else {
|
|
|
|
array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
|
|
|
|
}
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
2011-05-31 20:58:21 +00:00
|
|
|
void SetSubrange1(int offset, int len) {
|
|
|
|
subrange_offset1_ = offset;
|
|
|
|
}
|
|
|
|
void SetSubrange2(int offset, int len) {
|
|
|
|
subrange_offset2_ = offset;
|
|
|
|
}
|
2010-04-19 16:08:26 +00:00
|
|
|
|
|
|
|
Handle<JSArray> GetResult() {
|
2011-01-11 14:55:47 +00:00
|
|
|
return array_writer_.GetResult();
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2011-01-11 14:55:47 +00:00
|
|
|
static const int CHUNK_LEN_LIMIT = 800;
|
|
|
|
|
|
|
|
CompareOutputArrayWriter array_writer_;
|
2010-04-19 16:08:26 +00:00
|
|
|
LineEndsWrapper line_ends1_;
|
|
|
|
LineEndsWrapper line_ends2_;
|
2011-01-11 14:55:47 +00:00
|
|
|
Handle<String> s1_;
|
|
|
|
Handle<String> s2_;
|
2011-05-31 20:58:21 +00:00
|
|
|
int subrange_offset1_;
|
|
|
|
int subrange_offset2_;
|
2010-04-19 16:08:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-06-01 23:11:10 +00:00
|
|
|
Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
|
|
|
|
Handle<String> s2) {
|
2014-04-08 09:49:49 +00:00
|
|
|
s1 = String::Flatten(s1);
|
|
|
|
s2 = String::Flatten(s2);
|
2011-04-28 20:24:47 +00:00
|
|
|
|
2010-04-19 16:08:26 +00:00
|
|
|
LineEndsWrapper line_ends1(s1);
|
|
|
|
LineEndsWrapper line_ends2(s2);
|
|
|
|
|
2011-04-28 20:24:47 +00:00
|
|
|
LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
|
2011-06-01 23:11:10 +00:00
|
|
|
TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
|
2010-04-19 16:08:26 +00:00
|
|
|
|
2011-05-31 20:58:21 +00:00
|
|
|
NarrowDownInput(&input, &output);
|
|
|
|
|
2011-06-01 23:11:10 +00:00
|
|
|
Comparator::CalculateDifference(&input, &output);
|
2010-04-19 16:08:26 +00:00
|
|
|
|
2011-06-01 23:11:10 +00:00
|
|
|
return output.GetResult();
|
2010-04-19 16:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-24 19:59:09 +00:00
|
|
|
// Unwraps JSValue object, returning its field "value"
|
|
|
|
static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
|
2013-02-25 14:46:09 +00:00
|
|
|
return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
|
2010-02-24 19:59:09 +00:00
|
|
|
}
|
|
|
|
|
2010-10-04 11:35:46 +00:00
|
|
|
|
2010-02-24 19:59:09 +00:00
|
|
|
// Wraps any object into a OpaqueReference, that will hide the object
|
|
|
|
// from JavaScript.
|
2013-09-04 10:34:42 +00:00
|
|
|
static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) {
|
|
|
|
Isolate* isolate = object->GetIsolate();
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<JSFunction> constructor = isolate->opaque_reference_function();
|
2010-02-24 19:59:09 +00:00
|
|
|
Handle<JSValue> result =
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
|
2011-05-16 09:06:16 +00:00
|
|
|
result->set_value(*object);
|
2010-02-24 19:59:09 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-10-04 11:35:46 +00:00
|
|
|
|
2012-09-14 13:31:11 +00:00
|
|
|
static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
|
|
|
|
Handle<JSValue> jsValue) {
|
|
|
|
Object* shared = jsValue->value();
|
|
|
|
CHECK(shared->IsSharedFunctionInfo());
|
|
|
|
return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int GetArrayLength(Handle<JSArray> array) {
|
|
|
|
Object* length = array->length();
|
|
|
|
CHECK(length->IsSmi());
|
|
|
|
return Smi::cast(length)->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void FunctionInfoWrapper::SetInitialProperties(Handle<String> name,
|
|
|
|
int start_position,
|
|
|
|
int end_position,
|
|
|
|
int param_num,
|
|
|
|
int literal_count,
|
2014-04-30 14:33:35 +00:00
|
|
|
int slot_count,
|
2014-04-07 11:32:32 +00:00
|
|
|
int parent_index) {
|
|
|
|
HandleScope scope(isolate());
|
|
|
|
this->SetField(kFunctionNameOffset_, name);
|
|
|
|
this->SetSmiValueField(kStartPositionOffset_, start_position);
|
|
|
|
this->SetSmiValueField(kEndPositionOffset_, end_position);
|
|
|
|
this->SetSmiValueField(kParamNumOffset_, param_num);
|
|
|
|
this->SetSmiValueField(kLiteralNumOffset_, literal_count);
|
2014-04-30 14:33:35 +00:00
|
|
|
this->SetSmiValueField(kSlotNumOffset_, slot_count);
|
2014-04-07 11:32:32 +00:00
|
|
|
this->SetSmiValueField(kParentIndexOffset_, parent_index);
|
|
|
|
}
|
2010-10-04 11:35:46 +00:00
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void FunctionInfoWrapper::SetFunctionCode(Handle<Code> function_code,
|
|
|
|
Handle<HeapObject> code_scope_info) {
|
|
|
|
Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
|
|
|
|
this->SetField(kCodeOffset_, code_wrapper);
|
2010-02-24 19:59:09 +00:00
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
|
|
|
|
this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
|
|
|
|
}
|
2010-02-24 19:59:09 +00:00
|
|
|
|
2010-08-12 16:01:56 +00:00
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void FunctionInfoWrapper::SetSharedFunctionInfo(
|
|
|
|
Handle<SharedFunctionInfo> info) {
|
|
|
|
Handle<JSValue> info_holder = WrapInJSValue(info);
|
|
|
|
this->SetField(kSharedFunctionInfoOffset_, info_holder);
|
|
|
|
}
|
2010-02-24 19:59:09 +00:00
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<Code> FunctionInfoWrapper::GetFunctionCode() {
|
|
|
|
Handle<Object> element = this->GetField(kCodeOffset_);
|
|
|
|
Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
|
|
|
|
Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
|
|
|
|
CHECK(raw_result->IsCode());
|
|
|
|
return Handle<Code>::cast(raw_result);
|
|
|
|
}
|
2010-10-04 11:35:46 +00:00
|
|
|
|
2010-05-04 13:07:36 +00:00
|
|
|
|
2014-09-18 09:59:53 +00:00
|
|
|
Handle<TypeFeedbackVector> FunctionInfoWrapper::GetFeedbackVector() {
|
2014-04-30 14:33:35 +00:00
|
|
|
Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_);
|
2014-09-18 09:59:53 +00:00
|
|
|
Handle<TypeFeedbackVector> result;
|
2014-04-30 14:33:35 +00:00
|
|
|
if (element->IsJSValue()) {
|
|
|
|
Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
|
|
|
|
Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
|
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
Handle<SharedFunctionInfo>::cast(raw_result);
|
2014-09-18 09:59:53 +00:00
|
|
|
result = Handle<TypeFeedbackVector>(shared->feedback_vector(), isolate());
|
2014-04-30 14:33:35 +00:00
|
|
|
CHECK_EQ(result->length(), GetSlotCount());
|
|
|
|
} else {
|
|
|
|
// Scripts may never have a SharedFunctionInfo created, so
|
|
|
|
// create a type feedback vector here.
|
|
|
|
int slot_count = GetSlotCount();
|
|
|
|
result = isolate()->factory()->NewTypeFeedbackVector(slot_count);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<Object> FunctionInfoWrapper::GetCodeScopeInfo() {
|
|
|
|
Handle<Object> element = this->GetField(kCodeScopeInfoOffset_);
|
|
|
|
return UnwrapJSValue(Handle<JSValue>::cast(element));
|
|
|
|
}
|
2010-02-24 19:59:09 +00:00
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void SharedInfoWrapper::SetProperties(Handle<String> name,
|
|
|
|
int start_position,
|
|
|
|
int end_position,
|
|
|
|
Handle<SharedFunctionInfo> info) {
|
|
|
|
HandleScope scope(isolate());
|
|
|
|
this->SetField(kFunctionNameOffset_, name);
|
|
|
|
Handle<JSValue> info_holder = WrapInJSValue(info);
|
|
|
|
this->SetField(kSharedInfoOffset_, info_holder);
|
|
|
|
this->SetSmiValueField(kStartPositionOffset_, start_position);
|
|
|
|
this->SetSmiValueField(kEndPositionOffset_, end_position);
|
|
|
|
}
|
2010-03-05 22:08:58 +00:00
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
|
|
|
|
Handle<Object> element = this->GetField(kSharedInfoOffset_);
|
|
|
|
Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
|
|
|
|
return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
|
|
|
|
}
|
2010-02-24 19:59:09 +00:00
|
|
|
|
2010-10-04 11:35:46 +00:00
|
|
|
|
2010-03-05 22:08:58 +00:00
|
|
|
class FunctionInfoListener {
|
|
|
|
public:
|
2013-02-25 14:46:09 +00:00
|
|
|
explicit FunctionInfoListener(Isolate* isolate) {
|
2010-03-05 22:08:58 +00:00
|
|
|
current_parent_index_ = -1;
|
|
|
|
len_ = 0;
|
2013-02-25 14:46:09 +00:00
|
|
|
result_ = isolate->factory()->NewJSArray(10);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FunctionStarted(FunctionLiteral* fun) {
|
2013-02-25 14:46:09 +00:00
|
|
|
HandleScope scope(isolate());
|
2013-09-04 10:34:42 +00:00
|
|
|
FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate());
|
2010-03-05 22:08:58 +00:00
|
|
|
info.SetInitialProperties(fun->name(), fun->start_position(),
|
2011-11-09 13:54:26 +00:00
|
|
|
fun->end_position(), fun->parameter_count(),
|
2012-11-13 19:13:27 +00:00
|
|
|
fun->materialized_literal_count(),
|
2014-04-30 14:33:35 +00:00
|
|
|
fun->slot_count(),
|
2010-03-05 22:08:58 +00:00
|
|
|
current_parent_index_);
|
|
|
|
current_parent_index_ = len_;
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(result_, len_, info.GetJSArray());
|
2010-03-05 22:08:58 +00:00
|
|
|
len_++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FunctionDone() {
|
2013-02-25 14:46:09 +00:00
|
|
|
HandleScope scope(isolate());
|
2010-12-07 11:31:57 +00:00
|
|
|
FunctionInfoWrapper info =
|
|
|
|
FunctionInfoWrapper::cast(
|
2014-04-11 12:47:34 +00:00
|
|
|
*Object::GetElement(
|
|
|
|
isolate(), result_, current_parent_index_).ToHandleChecked());
|
2010-03-05 22:08:58 +00:00
|
|
|
current_parent_index_ = info.GetParentIndex();
|
|
|
|
}
|
|
|
|
|
2010-09-09 17:45:21 +00:00
|
|
|
// Saves only function code, because for a script function we
|
|
|
|
// may never create a SharedFunctionInfo object.
|
|
|
|
void FunctionCode(Handle<Code> function_code) {
|
2010-12-07 11:31:57 +00:00
|
|
|
FunctionInfoWrapper info =
|
|
|
|
FunctionInfoWrapper::cast(
|
2014-04-11 12:47:34 +00:00
|
|
|
*Object::GetElement(
|
|
|
|
isolate(), result_, current_parent_index_).ToHandleChecked());
|
2013-02-25 14:46:09 +00:00
|
|
|
info.SetFunctionCode(function_code,
|
2013-09-04 10:34:42 +00:00
|
|
|
Handle<HeapObject>(isolate()->heap()->null_value()));
|
2010-09-09 17:45:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Saves full information about a function: its code, its scope info
|
|
|
|
// and a SharedFunctionInfo object.
|
2012-06-11 12:42:31 +00:00
|
|
|
void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope,
|
|
|
|
Zone* zone) {
|
2010-09-09 17:45:21 +00:00
|
|
|
if (!shared->IsSharedFunctionInfo()) {
|
|
|
|
return;
|
|
|
|
}
|
2010-12-07 11:31:57 +00:00
|
|
|
FunctionInfoWrapper info =
|
|
|
|
FunctionInfoWrapper::cast(
|
2014-04-11 12:47:34 +00:00
|
|
|
*Object::GetElement(
|
|
|
|
isolate(), result_, current_parent_index_).ToHandleChecked());
|
2010-09-09 17:45:21 +00:00
|
|
|
info.SetFunctionCode(Handle<Code>(shared->code()),
|
2013-09-04 10:34:42 +00:00
|
|
|
Handle<HeapObject>(shared->scope_info()));
|
2010-09-09 17:45:21 +00:00
|
|
|
info.SetSharedFunctionInfo(shared);
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<Object> scope_info_list = SerializeFunctionScope(scope, zone);
|
2013-09-20 13:15:31 +00:00
|
|
|
info.SetFunctionScopeInfo(scope_info_list);
|
2010-09-09 17:45:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle<JSArray> GetResult() { return result_; }
|
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
private:
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate() const { return result_->GetIsolate(); }
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<Object> SerializeFunctionScope(Scope* scope, Zone* zone) {
|
2013-02-25 14:46:09 +00:00
|
|
|
Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10);
|
2010-03-05 22:08:58 +00:00
|
|
|
int scope_info_length = 0;
|
|
|
|
|
|
|
|
// Saves some description of scope. It stores name and indexes of
|
|
|
|
// variables in the whole scope chain. Null-named slots delimit
|
|
|
|
// scopes of this chain.
|
2013-09-20 13:15:31 +00:00
|
|
|
Scope* current_scope = scope;
|
|
|
|
while (current_scope != NULL) {
|
2014-04-07 11:32:32 +00:00
|
|
|
HandleScope handle_scope(isolate());
|
2013-09-20 13:15:31 +00:00
|
|
|
ZoneList<Variable*> stack_list(current_scope->StackLocalCount(), zone);
|
|
|
|
ZoneList<Variable*> context_list(
|
|
|
|
current_scope->ContextLocalCount(), zone);
|
|
|
|
current_scope->CollectStackAndContextLocals(&stack_list, &context_list);
|
2011-11-03 14:50:19 +00:00
|
|
|
context_list.Sort(&Variable::CompareIndex);
|
2010-03-05 22:08:58 +00:00
|
|
|
|
2011-11-03 14:50:19 +00:00
|
|
|
for (int i = 0; i < context_list.length(); i++) {
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(scope_info_list,
|
|
|
|
scope_info_length,
|
|
|
|
context_list[i]->name());
|
2010-03-05 22:08:58 +00:00
|
|
|
scope_info_length++;
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(
|
2011-03-08 11:14:25 +00:00
|
|
|
scope_info_list,
|
|
|
|
scope_info_length,
|
2013-02-25 14:46:09 +00:00
|
|
|
Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate()));
|
2010-03-05 22:08:58 +00:00
|
|
|
scope_info_length++;
|
|
|
|
}
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(scope_info_list,
|
|
|
|
scope_info_length,
|
|
|
|
Handle<Object>(isolate()->heap()->null_value(),
|
|
|
|
isolate()));
|
2010-03-05 22:08:58 +00:00
|
|
|
scope_info_length++;
|
|
|
|
|
2013-09-20 13:15:31 +00:00
|
|
|
current_scope = current_scope->outer_scope();
|
|
|
|
}
|
2010-03-05 22:08:58 +00:00
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
return scope_info_list;
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle<JSArray> result_;
|
|
|
|
int len_;
|
|
|
|
int current_parent_index_;
|
|
|
|
};
|
|
|
|
|
2010-10-04 11:35:46 +00:00
|
|
|
|
2014-06-11 13:40:18 +00:00
|
|
|
void LiveEdit::InitializeThreadLocal(Debug* debug) {
|
|
|
|
debug->thread_local_.frame_drop_mode_ = LiveEdit::FRAMES_UNTOUCHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LiveEdit::SetAfterBreakTarget(Debug* debug) {
|
2014-05-28 10:41:13 +00:00
|
|
|
Code* code = NULL;
|
2014-06-11 13:40:18 +00:00
|
|
|
Isolate* isolate = debug->isolate_;
|
|
|
|
switch (debug->thread_local_.frame_drop_mode_) {
|
2014-05-28 10:41:13 +00:00
|
|
|
case FRAMES_UNTOUCHED:
|
2014-06-11 13:40:18 +00:00
|
|
|
return false;
|
2014-05-28 10:41:13 +00:00
|
|
|
case FRAME_DROPPED_IN_IC_CALL:
|
|
|
|
// We must have been calling IC stub. Do not go there anymore.
|
|
|
|
code = isolate->builtins()->builtin(Builtins::kPlainReturn_LiveEdit);
|
|
|
|
break;
|
|
|
|
case FRAME_DROPPED_IN_DEBUG_SLOT_CALL:
|
|
|
|
// Debug break slot stub does not return normally, instead it manually
|
|
|
|
// cleans the stack and jumps. We should patch the jump address.
|
|
|
|
code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
|
|
|
|
break;
|
|
|
|
case FRAME_DROPPED_IN_DIRECT_CALL:
|
|
|
|
// Nothing to do, after_break_target is not used here.
|
2014-06-11 13:40:18 +00:00
|
|
|
return true;
|
2014-05-28 10:41:13 +00:00
|
|
|
case FRAME_DROPPED_IN_RETURN_CALL:
|
|
|
|
code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
|
|
|
|
break;
|
|
|
|
case CURRENTLY_SET_MODE:
|
|
|
|
UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
2014-06-11 13:40:18 +00:00
|
|
|
debug->after_break_target_ = code->entry();
|
|
|
|
return true;
|
2014-05-28 10:41:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
|
|
|
|
Handle<String> source) {
|
2013-09-04 10:34:42 +00:00
|
|
|
Isolate* isolate = script->GetIsolate();
|
2010-03-05 22:08:58 +00:00
|
|
|
|
2013-02-25 14:46:09 +00:00
|
|
|
FunctionInfoListener listener(isolate);
|
|
|
|
Handle<Object> original_source =
|
|
|
|
Handle<Object>(script->source(), isolate);
|
2010-03-05 22:08:58 +00:00
|
|
|
script->set_source(*source);
|
2011-03-18 20:35:07 +00:00
|
|
|
isolate->set_active_function_info_listener(&listener);
|
2012-12-03 21:47:39 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Creating verbose TryCatch from public API is currently the only way to
|
|
|
|
// force code save location. We do not use this the object directly.
|
|
|
|
v8::TryCatch try_catch;
|
|
|
|
try_catch.SetVerbose(true);
|
|
|
|
|
|
|
|
// A logical 'try' section.
|
2013-12-23 14:30:35 +00:00
|
|
|
Compiler::CompileForLiveEdit(script);
|
2012-12-03 21:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A logical 'catch' section.
|
2012-12-04 10:15:19 +00:00
|
|
|
Handle<JSObject> rethrow_exception;
|
2012-12-03 21:47:39 +00:00
|
|
|
if (isolate->has_pending_exception()) {
|
2014-04-08 09:44:24 +00:00
|
|
|
Handle<Object> exception(isolate->pending_exception(), isolate);
|
2012-12-03 21:47:39 +00:00
|
|
|
MessageLocation message_location = isolate->GetMessageLocation();
|
|
|
|
|
|
|
|
isolate->clear_pending_message();
|
|
|
|
isolate->clear_pending_exception();
|
|
|
|
|
|
|
|
// If possible, copy positions from message object to exception object.
|
|
|
|
if (exception->IsJSObject() && !message_location.script().is_null()) {
|
2012-12-04 10:15:19 +00:00
|
|
|
rethrow_exception = Handle<JSObject>::cast(exception);
|
2012-12-03 21:47:39 +00:00
|
|
|
|
|
|
|
Factory* factory = isolate->factory();
|
2013-02-28 17:03:34 +00:00
|
|
|
Handle<String> start_pos_key = factory->InternalizeOneByteString(
|
2014-09-10 12:38:12 +00:00
|
|
|
STATIC_CHAR_VECTOR("startPosition"));
|
|
|
|
Handle<String> end_pos_key =
|
|
|
|
factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition"));
|
|
|
|
Handle<String> script_obj_key =
|
|
|
|
factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject"));
|
2013-02-28 17:03:34 +00:00
|
|
|
Handle<Smi> start_pos(
|
|
|
|
Smi::FromInt(message_location.start_pos()), isolate);
|
2013-02-25 14:46:09 +00:00
|
|
|
Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
|
2014-04-16 13:28:11 +00:00
|
|
|
Handle<JSObject> script_obj =
|
|
|
|
Script::GetWrapper(message_location.script());
|
2014-07-22 08:28:49 +00:00
|
|
|
Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY)
|
|
|
|
.Assert();
|
|
|
|
Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY)
|
|
|
|
.Assert();
|
|
|
|
Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY)
|
2014-07-14 14:52:24 +00:00
|
|
|
.Assert();
|
2012-12-03 21:47:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A logical 'finally' section.
|
2011-03-18 20:35:07 +00:00
|
|
|
isolate->set_active_function_info_listener(NULL);
|
2010-03-05 22:08:58 +00:00
|
|
|
script->set_source(*original_source);
|
|
|
|
|
2012-12-03 21:47:39 +00:00
|
|
|
if (rethrow_exception.is_null()) {
|
2014-04-07 11:32:32 +00:00
|
|
|
return listener.GetResult();
|
2012-12-03 21:47:39 +00:00
|
|
|
} else {
|
2014-04-07 11:32:32 +00:00
|
|
|
return isolate->Throw<JSArray>(rethrow_exception);
|
2012-12-03 21:47:39 +00:00
|
|
|
}
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
|
2013-09-04 07:05:11 +00:00
|
|
|
Isolate* isolate = array->GetIsolate();
|
|
|
|
HandleScope scope(isolate);
|
2012-09-14 13:31:11 +00:00
|
|
|
int len = GetArrayLength(array);
|
2010-03-05 22:08:58 +00:00
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
Handle<SharedFunctionInfo> info(
|
2013-09-04 07:05:11 +00:00
|
|
|
SharedFunctionInfo::cast(
|
2014-04-11 12:47:34 +00:00
|
|
|
*Object::GetElement(isolate, array, i).ToHandleChecked()));
|
2013-09-04 10:34:42 +00:00
|
|
|
SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create(isolate);
|
2010-03-05 22:08:58 +00:00
|
|
|
Handle<String> name_handle(String::cast(info->name()));
|
|
|
|
info_wrapper.SetProperties(name_handle, info->start_position(),
|
|
|
|
info->end_position(), info);
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(array, i, info_wrapper.GetJSArray());
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-16 21:40:35 +00:00
|
|
|
// Visitor that finds all references to a particular code object,
|
|
|
|
// including "CODE_TARGET" references in other code objects and replaces
|
|
|
|
// them on the fly.
|
|
|
|
class ReplacingVisitor : public ObjectVisitor {
|
2010-03-15 21:06:51 +00:00
|
|
|
public:
|
2012-06-16 21:40:35 +00:00
|
|
|
explicit ReplacingVisitor(Code* original, Code* substitution)
|
|
|
|
: original_(original), substitution_(substitution) {
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void VisitPointers(Object** start, Object** end) {
|
|
|
|
for (Object** p = start; p < end; p++) {
|
|
|
|
if (*p == original_) {
|
2012-06-16 21:40:35 +00:00
|
|
|
*p = substitution_;
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-20 07:10:18 +00:00
|
|
|
virtual void VisitCodeEntry(Address entry) {
|
|
|
|
if (Code::GetObjectFromEntryAddress(entry) == original_) {
|
2012-06-16 21:40:35 +00:00
|
|
|
Address substitution_entry = substitution_->instruction_start();
|
|
|
|
Memory::Address_at(entry) = substitution_entry;
|
2010-08-20 07:10:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void VisitCodeTarget(RelocInfo* rinfo) {
|
2010-04-21 16:59:58 +00:00
|
|
|
if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
|
|
|
|
Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
|
2012-06-16 21:40:35 +00:00
|
|
|
Address substitution_entry = substitution_->instruction_start();
|
|
|
|
rinfo->set_target_address(substitution_entry);
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void VisitDebugTarget(RelocInfo* rinfo) {
|
|
|
|
VisitCodeTarget(rinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Code* original_;
|
2012-06-16 21:40:35 +00:00
|
|
|
Code* substitution_;
|
2010-03-15 21:06:51 +00:00
|
|
|
};
|
|
|
|
|
2010-03-20 22:37:15 +00:00
|
|
|
|
2010-03-15 21:06:51 +00:00
|
|
|
// Finds all references to original and replaces them with substitution.
|
2012-07-01 22:25:48 +00:00
|
|
|
static void ReplaceCodeObject(Handle<Code> original,
|
|
|
|
Handle<Code> substitution) {
|
|
|
|
// Perform a full GC in order to ensure that we are not in the middle of an
|
|
|
|
// incremental marking phase when we are replacing the code object.
|
|
|
|
// Since we are not in an incremental marking phase we can write pointers
|
|
|
|
// to code objects (that are never in new space) without worrying about
|
|
|
|
// write barriers.
|
2013-02-11 13:02:20 +00:00
|
|
|
Heap* heap = original->GetHeap();
|
2014-05-22 11:13:37 +00:00
|
|
|
HeapIterator iterator(heap);
|
2012-07-01 22:25:48 +00:00
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(!heap->InNewSpace(*substitution));
|
2010-03-15 21:06:51 +00:00
|
|
|
|
2012-07-01 22:25:48 +00:00
|
|
|
ReplacingVisitor visitor(*original, *substitution);
|
2010-03-15 21:06:51 +00:00
|
|
|
|
|
|
|
// Iterate over all roots. Stack frames may have pointer into original code,
|
|
|
|
// so temporary replace the pointers with offset numbers
|
|
|
|
// in prologue/epilogue.
|
2013-02-11 13:02:20 +00:00
|
|
|
heap->IterateRoots(&visitor, VISIT_ALL);
|
2010-03-15 21:06:51 +00:00
|
|
|
|
|
|
|
// Now iterate over all pointers of all objects, including code_target
|
|
|
|
// implicit pointers.
|
|
|
|
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
|
|
|
obj->Iterate(&visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-13 19:13:27 +00:00
|
|
|
// Patch function literals.
|
|
|
|
// Name 'literals' is a misnomer. Rather it's a cache for complex object
|
|
|
|
// boilerplates and for a native context. We must clean cached values.
|
|
|
|
// Additionally we may need to allocate a new array if number of literals
|
|
|
|
// changed.
|
|
|
|
class LiteralFixer {
|
|
|
|
public:
|
|
|
|
static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
|
|
|
|
Handle<SharedFunctionInfo> shared_info,
|
|
|
|
Isolate* isolate) {
|
|
|
|
int new_literal_count = compile_info_wrapper->GetLiteralCount();
|
|
|
|
if (new_literal_count > 0) {
|
|
|
|
new_literal_count += JSFunction::kLiteralsPrefixSize;
|
|
|
|
}
|
|
|
|
int old_literal_count = shared_info->num_literals();
|
|
|
|
|
|
|
|
if (old_literal_count == new_literal_count) {
|
|
|
|
// If literal count didn't change, simply go over all functions
|
|
|
|
// and clear literal arrays.
|
|
|
|
ClearValuesVisitor visitor;
|
2014-05-22 11:13:37 +00:00
|
|
|
IterateJSFunctions(shared_info, &visitor);
|
2012-11-13 19:13:27 +00:00
|
|
|
} else {
|
|
|
|
// When literal count changes, we have to create new array instances.
|
|
|
|
// Since we cannot create instances when iterating heap, we should first
|
|
|
|
// collect all functions and fix their literal arrays.
|
|
|
|
Handle<FixedArray> function_instances =
|
|
|
|
CollectJSFunctions(shared_info, isolate);
|
|
|
|
for (int i = 0; i < function_instances->length(); i++) {
|
|
|
|
Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
|
|
|
|
Handle<FixedArray> old_literals(fun->literals());
|
|
|
|
Handle<FixedArray> new_literals =
|
|
|
|
isolate->factory()->NewFixedArray(new_literal_count);
|
|
|
|
if (new_literal_count > 0) {
|
|
|
|
Handle<Context> native_context;
|
|
|
|
if (old_literals->length() >
|
|
|
|
JSFunction::kLiteralNativeContextIndex) {
|
|
|
|
native_context = Handle<Context>(
|
|
|
|
JSFunction::NativeContextFromLiterals(fun->literals()));
|
|
|
|
} else {
|
|
|
|
native_context = Handle<Context>(fun->context()->native_context());
|
|
|
|
}
|
|
|
|
new_literals->set(JSFunction::kLiteralNativeContextIndex,
|
|
|
|
*native_context);
|
|
|
|
}
|
|
|
|
fun->set_literals(*new_literals);
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_info->set_num_literals(new_literal_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Iterates all function instances in the HEAP that refers to the
|
|
|
|
// provided shared_info.
|
|
|
|
template<typename Visitor>
|
2014-05-22 11:13:37 +00:00
|
|
|
static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,
|
2012-11-13 19:13:27 +00:00
|
|
|
Visitor* visitor) {
|
2013-02-11 13:02:20 +00:00
|
|
|
HeapIterator iterator(shared_info->GetHeap());
|
2012-11-13 19:13:27 +00:00
|
|
|
for (HeapObject* obj = iterator.next(); obj != NULL;
|
|
|
|
obj = iterator.next()) {
|
|
|
|
if (obj->IsJSFunction()) {
|
|
|
|
JSFunction* function = JSFunction::cast(obj);
|
2014-05-22 11:13:37 +00:00
|
|
|
if (function->shared() == *shared_info) {
|
2012-11-13 19:13:27 +00:00
|
|
|
visitor->visit(function);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds all instances of JSFunction that refers to the provided shared_info
|
|
|
|
// and returns array with them.
|
|
|
|
static Handle<FixedArray> CollectJSFunctions(
|
|
|
|
Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
|
|
|
|
CountVisitor count_visitor;
|
|
|
|
count_visitor.count = 0;
|
2014-05-22 11:13:37 +00:00
|
|
|
IterateJSFunctions(shared_info, &count_visitor);
|
2012-11-13 19:13:27 +00:00
|
|
|
int size = count_visitor.count;
|
|
|
|
|
|
|
|
Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
|
|
|
|
if (size > 0) {
|
|
|
|
CollectVisitor collect_visitor(result);
|
2014-05-22 11:13:37 +00:00
|
|
|
IterateJSFunctions(shared_info, &collect_visitor);
|
2012-11-13 19:13:27 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
class ClearValuesVisitor {
|
|
|
|
public:
|
|
|
|
void visit(JSFunction* fun) {
|
|
|
|
FixedArray* literals = fun->literals();
|
|
|
|
int len = literals->length();
|
|
|
|
for (int j = JSFunction::kLiteralsPrefixSize; j < len; j++) {
|
|
|
|
literals->set_undefined(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CountVisitor {
|
|
|
|
public:
|
|
|
|
void visit(JSFunction* fun) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
int count;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CollectVisitor {
|
|
|
|
public:
|
|
|
|
explicit CollectVisitor(Handle<FixedArray> output)
|
|
|
|
: m_output(output), m_pos(0) {}
|
|
|
|
|
|
|
|
void visit(JSFunction* fun) {
|
|
|
|
m_output->set(m_pos, fun);
|
|
|
|
m_pos++;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
Handle<FixedArray> m_output;
|
|
|
|
int m_pos;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-04-09 13:51:11 +00:00
|
|
|
// Check whether the code is natural function code (not a lazy-compile stub
|
|
|
|
// code).
|
|
|
|
static bool IsJSFunctionCode(Code* code) {
|
|
|
|
return code->kind() == Code::FUNCTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-07 11:31:57 +00:00
|
|
|
// Returns true if an instance of candidate were inlined into function's code.
|
|
|
|
static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
|
2013-06-03 15:32:22 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2010-12-07 11:31:57 +00:00
|
|
|
|
|
|
|
if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
|
|
|
|
|
|
|
|
DeoptimizationInputData* data =
|
|
|
|
DeoptimizationInputData::cast(function->code()->deoptimization_data());
|
|
|
|
|
2013-09-10 14:30:36 +00:00
|
|
|
if (data == function->GetIsolate()->heap()->empty_fixed_array()) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-12-07 11:31:57 +00:00
|
|
|
|
|
|
|
FixedArray* literals = data->LiteralArray();
|
|
|
|
|
|
|
|
int inlined_count = data->InlinedFunctionCount()->value();
|
|
|
|
for (int i = 0; i < inlined_count; ++i) {
|
|
|
|
JSFunction* inlined = JSFunction::cast(literals->get(i));
|
|
|
|
if (inlined->shared() == candidate) return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-04 15:06:36 +00:00
|
|
|
// Marks code that shares the same shared function info or has inlined
|
|
|
|
// code that shares the same function info.
|
|
|
|
class DependentFunctionMarker: public OptimizedFunctionVisitor {
|
|
|
|
public:
|
|
|
|
SharedFunctionInfo* shared_info_;
|
|
|
|
bool found_;
|
|
|
|
|
|
|
|
explicit DependentFunctionMarker(SharedFunctionInfo* shared_info)
|
|
|
|
: shared_info_(shared_info), found_(false) { }
|
|
|
|
|
|
|
|
virtual void EnterContext(Context* context) { } // Don't care.
|
|
|
|
virtual void LeaveContext(Context* context) { } // Don't care.
|
|
|
|
virtual void VisitFunction(JSFunction* function) {
|
|
|
|
// It should be guaranteed by the iterator that everything is optimized.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
|
2013-09-04 15:06:36 +00:00
|
|
|
if (shared_info_ == function->shared() ||
|
|
|
|
IsInlined(function, shared_info_)) {
|
|
|
|
// Mark the code for deoptimization.
|
|
|
|
function->code()->set_marked_for_deoptimization(true);
|
|
|
|
found_ = true;
|
2013-09-04 13:53:24 +00:00
|
|
|
}
|
2013-09-04 15:06:36 +00:00
|
|
|
}
|
|
|
|
};
|
2010-12-07 11:31:57 +00:00
|
|
|
|
|
|
|
|
2013-09-04 15:06:36 +00:00
|
|
|
static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
|
2013-06-03 15:32:22 +00:00
|
|
|
DisallowHeapAllocation no_allocation;
|
2013-09-04 13:53:24 +00:00
|
|
|
DependentFunctionMarker marker(function_info);
|
|
|
|
// TODO(titzer): need to traverse all optimized code to find OSR code here.
|
|
|
|
Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker);
|
2010-12-07 11:31:57 +00:00
|
|
|
|
2013-09-04 13:53:24 +00:00
|
|
|
if (marker.found_) {
|
|
|
|
// Only go through with the deoptimization if something was found.
|
|
|
|
Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate());
|
|
|
|
}
|
2010-12-07 11:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void LiveEdit::ReplaceFunctionCode(
|
2010-10-25 15:22:03 +00:00
|
|
|
Handle<JSArray> new_compile_info_array,
|
|
|
|
Handle<JSArray> shared_info_array) {
|
2013-09-04 10:34:42 +00:00
|
|
|
Isolate* isolate = new_compile_info_array->GetIsolate();
|
2010-05-04 13:07:36 +00:00
|
|
|
|
2010-03-05 22:08:58 +00:00
|
|
|
FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
|
|
|
|
SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
|
|
|
|
2010-04-09 13:51:11 +00:00
|
|
|
if (IsJSFunctionCode(shared_info->code())) {
|
2011-03-30 10:46:55 +00:00
|
|
|
Handle<Code> code = compile_info_wrapper.GetFunctionCode();
|
2012-07-01 22:25:48 +00:00
|
|
|
ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
|
|
|
|
Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
|
2010-08-12 16:01:56 +00:00
|
|
|
if (code_scope_info->IsFixedArray()) {
|
2011-11-03 10:36:55 +00:00
|
|
|
shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
|
2010-08-12 16:01:56 +00:00
|
|
|
}
|
2013-08-07 09:33:09 +00:00
|
|
|
shared_info->DisableOptimization(kLiveEdit);
|
2014-04-30 14:33:35 +00:00
|
|
|
// Update the type feedback vector
|
2014-09-18 09:59:53 +00:00
|
|
|
Handle<TypeFeedbackVector> feedback_vector =
|
2014-04-30 14:33:35 +00:00
|
|
|
compile_info_wrapper.GetFeedbackVector();
|
|
|
|
shared_info->set_feedback_vector(*feedback_vector);
|
2010-04-09 13:51:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (shared_info->debug_info()->IsDebugInfo()) {
|
|
|
|
Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
|
|
|
|
Handle<Code> new_original_code =
|
2013-06-04 10:30:05 +00:00
|
|
|
isolate->factory()->CopyCode(compile_info_wrapper.GetFunctionCode());
|
2010-04-09 13:51:11 +00:00
|
|
|
debug_info->set_original_code(*new_original_code);
|
|
|
|
}
|
2010-03-15 21:06:51 +00:00
|
|
|
|
2011-03-30 10:46:55 +00:00
|
|
|
int start_position = compile_info_wrapper.GetStartPosition();
|
|
|
|
int end_position = compile_info_wrapper.GetEndPosition();
|
|
|
|
shared_info->set_start_position(start_position);
|
|
|
|
shared_info->set_end_position(end_position);
|
2010-04-09 13:51:11 +00:00
|
|
|
|
2012-11-13 19:13:27 +00:00
|
|
|
LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
|
|
|
|
|
2010-04-09 13:51:11 +00:00
|
|
|
shared_info->set_construct_stub(
|
2013-02-15 09:27:10 +00:00
|
|
|
isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric));
|
2010-05-04 13:07:36 +00:00
|
|
|
|
2010-12-07 11:31:57 +00:00
|
|
|
DeoptimizeDependentFunctions(*shared_info);
|
2013-02-15 09:27:10 +00:00
|
|
|
isolate->compilation_cache()->Remove(shared_info);
|
2010-12-07 11:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array) {
|
2010-12-07 11:31:57 +00:00
|
|
|
SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
|
|
|
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
|
|
|
|
|
|
|
DeoptimizeDependentFunctions(*shared_info);
|
2014-04-07 11:32:32 +00:00
|
|
|
shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
|
|
|
|
Handle<Object> script_handle) {
|
|
|
|
Handle<SharedFunctionInfo> shared_info =
|
2012-09-14 13:31:11 +00:00
|
|
|
UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
|
2012-09-10 08:35:26 +00:00
|
|
|
CHECK(script_handle->IsScript() || script_handle->IsUndefined());
|
2010-03-05 22:08:58 +00:00
|
|
|
shared_info->set_script(*script_handle);
|
2010-12-07 11:31:57 +00:00
|
|
|
|
2013-09-04 10:34:42 +00:00
|
|
|
function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For a script text change (defined as position_change_array), translates
|
|
|
|
// position in unchanged text to position in changed text.
|
|
|
|
// Text change is a set of non-overlapping regions in text, that have changed
|
|
|
|
// their contents and length. It is specified as array of groups of 3 numbers:
|
|
|
|
// (change_begin, change_end, change_end_new_position).
|
|
|
|
// Each group describes a change in text; groups are sorted by change_begin.
|
|
|
|
// Only position in text beyond any changes may be successfully translated.
|
|
|
|
// If a positions is inside some region that changed, result is currently
|
|
|
|
// undefined.
|
|
|
|
static int TranslatePosition(int original_position,
|
|
|
|
Handle<JSArray> position_change_array) {
|
|
|
|
int position_diff = 0;
|
2012-09-14 13:31:11 +00:00
|
|
|
int array_len = GetArrayLength(position_change_array);
|
2013-09-04 07:05:11 +00:00
|
|
|
Isolate* isolate = position_change_array->GetIsolate();
|
2010-03-06 01:21:34 +00:00
|
|
|
// TODO(635): binary search may be used here
|
2010-03-05 22:08:58 +00:00
|
|
|
for (int i = 0; i < array_len; i += 3) {
|
2014-03-20 12:22:13 +00:00
|
|
|
HandleScope scope(isolate);
|
2014-04-11 12:47:34 +00:00
|
|
|
Handle<Object> element = Object::GetElement(
|
|
|
|
isolate, position_change_array, i).ToHandleChecked();
|
2012-09-14 13:31:11 +00:00
|
|
|
CHECK(element->IsSmi());
|
2014-03-20 12:22:13 +00:00
|
|
|
int chunk_start = Handle<Smi>::cast(element)->value();
|
2010-03-05 22:08:58 +00:00
|
|
|
if (original_position < chunk_start) {
|
|
|
|
break;
|
|
|
|
}
|
2014-04-11 12:47:34 +00:00
|
|
|
element = Object::GetElement(
|
|
|
|
isolate, position_change_array, i + 1).ToHandleChecked();
|
2012-09-14 13:31:11 +00:00
|
|
|
CHECK(element->IsSmi());
|
2014-03-20 12:22:13 +00:00
|
|
|
int chunk_end = Handle<Smi>::cast(element)->value();
|
2010-03-05 22:08:58 +00:00
|
|
|
// Position mustn't be inside a chunk.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(original_position >= chunk_end);
|
2014-04-11 12:47:34 +00:00
|
|
|
element = Object::GetElement(
|
|
|
|
isolate, position_change_array, i + 2).ToHandleChecked();
|
2012-09-14 13:31:11 +00:00
|
|
|
CHECK(element->IsSmi());
|
2014-03-20 12:22:13 +00:00
|
|
|
int chunk_changed_end = Handle<Smi>::cast(element)->value();
|
2010-03-15 21:06:51 +00:00
|
|
|
position_diff = chunk_changed_end - chunk_end;
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return original_position + position_diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-15 21:06:51 +00:00
|
|
|
// Auto-growing buffer for writing relocation info code section. This buffer
|
|
|
|
// is a simplified version of buffer from Assembler. Unlike Assembler, this
|
|
|
|
// class is platform-independent and it works without dealing with instructions.
|
|
|
|
// As specified by RelocInfo format, the buffer is filled in reversed order:
|
|
|
|
// from upper to lower addresses.
|
|
|
|
// It uses NewArray/DeleteArray for memory management.
|
|
|
|
class RelocInfoBuffer {
|
|
|
|
public:
|
|
|
|
RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
|
|
|
|
buffer_size_ = buffer_initial_capicity + kBufferGap;
|
|
|
|
buffer_ = NewArray<byte>(buffer_size_);
|
|
|
|
|
|
|
|
reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
|
|
|
|
}
|
|
|
|
~RelocInfoBuffer() {
|
|
|
|
DeleteArray(buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// As specified by RelocInfo format, the buffer is filled in reversed order:
|
|
|
|
// from upper to lower addresses.
|
|
|
|
void Write(const RelocInfo* rinfo) {
|
|
|
|
if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
|
|
|
|
Grow();
|
|
|
|
}
|
|
|
|
reloc_info_writer_.Write(rinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector<byte> GetResult() {
|
|
|
|
// Return the bytes from pos up to end of buffer.
|
2010-04-13 11:59:37 +00:00
|
|
|
int result_size =
|
|
|
|
static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
|
|
|
|
return Vector<byte>(reloc_info_writer_.pos(), result_size);
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void Grow() {
|
|
|
|
// Compute new buffer size.
|
|
|
|
int new_buffer_size;
|
|
|
|
if (buffer_size_ < 2 * KB) {
|
|
|
|
new_buffer_size = 4 * KB;
|
|
|
|
} else {
|
|
|
|
new_buffer_size = 2 * buffer_size_;
|
|
|
|
}
|
|
|
|
// Some internal data structures overflow for very large buffers,
|
|
|
|
// they must ensure that kMaximalBufferSize is not too large.
|
|
|
|
if (new_buffer_size > kMaximalBufferSize) {
|
|
|
|
V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
|
|
|
|
}
|
|
|
|
|
2012-01-13 13:09:52 +00:00
|
|
|
// Set up new buffer.
|
2010-03-15 21:06:51 +00:00
|
|
|
byte* new_buffer = NewArray<byte>(new_buffer_size);
|
|
|
|
|
|
|
|
// Copy the data.
|
2010-04-13 11:59:37 +00:00
|
|
|
int curently_used_size =
|
|
|
|
static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
|
2014-05-27 07:57:22 +00:00
|
|
|
MemMove(new_buffer + new_buffer_size - curently_used_size,
|
|
|
|
reloc_info_writer_.pos(), curently_used_size);
|
2010-03-15 21:06:51 +00:00
|
|
|
|
|
|
|
reloc_info_writer_.Reposition(
|
|
|
|
new_buffer + new_buffer_size - curently_used_size,
|
|
|
|
reloc_info_writer_.last_pc());
|
|
|
|
|
|
|
|
DeleteArray(buffer_);
|
|
|
|
buffer_ = new_buffer;
|
|
|
|
buffer_size_ = new_buffer_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
RelocInfoWriter reloc_info_writer_;
|
|
|
|
byte* buffer_;
|
|
|
|
int buffer_size_;
|
|
|
|
|
2010-05-20 08:59:36 +00:00
|
|
|
static const int kBufferGap = RelocInfoWriter::kMaxSize;
|
2010-03-15 21:06:51 +00:00
|
|
|
static const int kMaximalBufferSize = 512*MB;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2010-03-15 21:06:51 +00:00
|
|
|
// Patch positions in code (changes relocation info section) and possibly
|
|
|
|
// returns new instance of code.
|
2011-09-19 18:36:47 +00:00
|
|
|
static Handle<Code> PatchPositionsInCode(
|
|
|
|
Handle<Code> code,
|
2010-03-15 21:06:51 +00:00
|
|
|
Handle<JSArray> position_change_array) {
|
2013-06-04 10:30:05 +00:00
|
|
|
Isolate* isolate = code->GetIsolate();
|
2010-03-15 21:06:51 +00:00
|
|
|
|
|
|
|
RelocInfoBuffer buffer_writer(code->relocation_size(),
|
|
|
|
code->instruction_start());
|
|
|
|
|
|
|
|
{
|
|
|
|
for (RelocIterator it(*code); !it.done(); it.next()) {
|
|
|
|
RelocInfo* rinfo = it.rinfo();
|
|
|
|
if (RelocInfo::IsPosition(rinfo->rmode())) {
|
|
|
|
int position = static_cast<int>(rinfo->data());
|
|
|
|
int new_position = TranslatePosition(position,
|
|
|
|
position_change_array);
|
|
|
|
if (position != new_position) {
|
2011-09-19 18:36:47 +00:00
|
|
|
RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
|
2010-03-15 21:06:51 +00:00
|
|
|
buffer_writer.Write(&info_copy);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2012-11-08 12:18:11 +00:00
|
|
|
if (RelocInfo::IsRealRelocMode(rinfo->rmode())) {
|
|
|
|
buffer_writer.Write(it.rinfo());
|
|
|
|
}
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector<byte> buffer = buffer_writer.GetResult();
|
|
|
|
|
|
|
|
if (buffer.length() == code->relocation_size()) {
|
|
|
|
// Simply patch relocation area of code.
|
2014-05-27 07:57:22 +00:00
|
|
|
MemCopy(code->relocation_start(), buffer.start(), buffer.length());
|
2010-03-15 21:06:51 +00:00
|
|
|
return code;
|
|
|
|
} else {
|
|
|
|
// Relocation info section now has different size. We cannot simply
|
|
|
|
// rewrite it inside code object. Instead we have to create a new
|
|
|
|
// code object.
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<Code> result(isolate->factory()->CopyCode(code, buffer));
|
2010-03-15 21:06:51 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
|
|
|
|
Handle<JSArray> position_change_array) {
|
2010-03-05 22:08:58 +00:00
|
|
|
SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
|
|
|
Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
|
|
|
|
|
2010-04-08 12:37:10 +00:00
|
|
|
int old_function_start = info->start_position();
|
|
|
|
int new_function_start = TranslatePosition(old_function_start,
|
|
|
|
position_change_array);
|
2011-03-30 10:46:55 +00:00
|
|
|
int new_function_end = TranslatePosition(info->end_position(),
|
|
|
|
position_change_array);
|
|
|
|
int new_function_token_pos =
|
|
|
|
TranslatePosition(info->function_token_position(), position_change_array);
|
2010-03-05 22:08:58 +00:00
|
|
|
|
2011-03-30 10:46:55 +00:00
|
|
|
info->set_start_position(new_function_start);
|
|
|
|
info->set_end_position(new_function_end);
|
|
|
|
info->set_function_token_position(new_function_token_pos);
|
2010-03-15 21:06:51 +00:00
|
|
|
|
2010-04-09 13:51:11 +00:00
|
|
|
if (IsJSFunctionCode(info->code())) {
|
|
|
|
// Patch relocation info section of the code.
|
|
|
|
Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
|
|
|
|
position_change_array);
|
|
|
|
if (*patched_code != info->code()) {
|
|
|
|
// Replace all references to the code across the heap. In particular,
|
|
|
|
// some stubs may refer to this code and this code may be being executed
|
|
|
|
// on stack (it is safe to substitute the code object on stack, because
|
|
|
|
// we only change the structure of rinfo and leave instructions
|
|
|
|
// untouched).
|
2012-07-01 22:25:48 +00:00
|
|
|
ReplaceCodeObject(Handle<Code>(info->code()), patched_code);
|
2010-04-09 13:51:11 +00:00
|
|
|
}
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
2010-04-28 11:38:43 +00:00
|
|
|
}
|
2010-03-15 21:06:51 +00:00
|
|
|
|
2010-04-08 12:37:10 +00:00
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
static Handle<Script> CreateScriptCopy(Handle<Script> original) {
|
2013-06-04 10:30:05 +00:00
|
|
|
Isolate* isolate = original->GetIsolate();
|
2010-04-08 12:37:10 +00:00
|
|
|
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<String> original_source(String::cast(original->source()));
|
|
|
|
Handle<Script> copy = isolate->factory()->NewScript(original_source);
|
2010-03-15 21:06:51 +00:00
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
copy->set_name(original->name());
|
|
|
|
copy->set_line_offset(original->line_offset());
|
|
|
|
copy->set_column_offset(original->column_offset());
|
|
|
|
copy->set_type(original->type());
|
|
|
|
copy->set_context_data(original->context_data());
|
|
|
|
copy->set_eval_from_shared(original->eval_from_shared());
|
|
|
|
copy->set_eval_from_instructions_offset(
|
|
|
|
original->eval_from_instructions_offset());
|
|
|
|
|
2013-07-30 17:00:05 +00:00
|
|
|
// Copy all the flags, but clear compilation state.
|
|
|
|
copy->set_flags(original->flags());
|
|
|
|
copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
|
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script,
|
|
|
|
Handle<String> new_source,
|
|
|
|
Handle<Object> old_script_name) {
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate = original_script->GetIsolate();
|
2010-04-28 11:38:43 +00:00
|
|
|
Handle<Object> old_script_object;
|
|
|
|
if (old_script_name->IsString()) {
|
|
|
|
Handle<Script> old_script = CreateScriptCopy(original_script);
|
|
|
|
old_script->set_name(String::cast(*old_script_name));
|
|
|
|
old_script_object = old_script;
|
2014-06-26 15:12:04 +00:00
|
|
|
isolate->debug()->OnAfterCompile(old_script);
|
2010-04-28 11:38:43 +00:00
|
|
|
} else {
|
2013-02-25 14:46:09 +00:00
|
|
|
old_script_object = isolate->factory()->null_value();
|
2010-04-28 11:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
original_script->set_source(*new_source);
|
|
|
|
|
|
|
|
// Drop line ends so that they will be recalculated.
|
2013-09-10 14:30:36 +00:00
|
|
|
original_script->set_line_ends(isolate->heap()->undefined_value());
|
2010-04-28 11:38:43 +00:00
|
|
|
|
2014-04-07 11:32:32 +00:00
|
|
|
return old_script_object;
|
2010-04-28 11:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void LiveEdit::ReplaceRefToNestedFunction(
|
|
|
|
Handle<JSValue> parent_function_wrapper,
|
|
|
|
Handle<JSValue> orig_function_wrapper,
|
|
|
|
Handle<JSValue> subst_function_wrapper) {
|
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> parent_shared =
|
2012-09-14 13:31:11 +00:00
|
|
|
UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
|
2010-04-28 11:38:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig_shared =
|
2012-09-14 13:31:11 +00:00
|
|
|
UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
|
2010-04-28 11:38:43 +00:00
|
|
|
Handle<SharedFunctionInfo> subst_shared =
|
2012-09-14 13:31:11 +00:00
|
|
|
UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
|
2010-04-28 11:38:43 +00:00
|
|
|
|
|
|
|
for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
|
|
|
|
if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
|
|
|
|
if (it.rinfo()->target_object() == *orig_shared) {
|
|
|
|
it.rinfo()->set_target_object(*subst_shared);
|
2010-04-08 12:37:10 +00:00
|
|
|
}
|
2010-03-15 21:06:51 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-06 17:58:28 +00:00
|
|
|
// Check an activation against list of functions. If there is a function
|
|
|
|
// that matches, its status in result array is changed to status argument value.
|
|
|
|
static bool CheckActivation(Handle<JSArray> shared_info_array,
|
2010-12-07 11:31:57 +00:00
|
|
|
Handle<JSArray> result,
|
|
|
|
StackFrame* frame,
|
2010-04-06 17:58:28 +00:00
|
|
|
LiveEdit::FunctionPatchabilityStatus status) {
|
2010-12-07 11:31:57 +00:00
|
|
|
if (!frame->is_java_script()) return false;
|
|
|
|
|
2013-07-11 16:45:58 +00:00
|
|
|
Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
|
2010-12-07 11:31:57 +00:00
|
|
|
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate = shared_info_array->GetIsolate();
|
2012-09-14 13:31:11 +00:00
|
|
|
int len = GetArrayLength(shared_info_array);
|
2010-04-06 17:58:28 +00:00
|
|
|
for (int i = 0; i < len; i++) {
|
2014-03-20 12:22:13 +00:00
|
|
|
HandleScope scope(isolate);
|
|
|
|
Handle<Object> element =
|
2014-04-11 12:47:34 +00:00
|
|
|
Object::GetElement(isolate, shared_info_array, i).ToHandleChecked();
|
2014-03-20 12:22:13 +00:00
|
|
|
Handle<JSValue> jsvalue = Handle<JSValue>::cast(element);
|
2012-09-14 13:31:11 +00:00
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
UnwrapSharedFunctionInfoFromJSValue(jsvalue);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
2010-12-07 11:31:57 +00:00
|
|
|
if (function->shared() == *shared || IsInlined(*function, *shared)) {
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
|
2010-04-06 17:58:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Iterates over handler chain and removes all elements that are inside
|
|
|
|
// frames being dropped.
|
|
|
|
static bool FixTryCatchHandler(StackFrame* top_frame,
|
|
|
|
StackFrame* bottom_frame) {
|
|
|
|
Address* pointer_address =
|
2013-09-04 10:34:42 +00:00
|
|
|
&Memory::Address_at(top_frame->isolate()->get_address_from_id(
|
2011-09-08 16:29:57 +00:00
|
|
|
Isolate::kHandlerAddress));
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
while (*pointer_address < top_frame->sp()) {
|
|
|
|
pointer_address = &Memory::Address_at(*pointer_address);
|
|
|
|
}
|
|
|
|
Address* above_frame_address = pointer_address;
|
|
|
|
while (*pointer_address < bottom_frame->fp()) {
|
|
|
|
pointer_address = &Memory::Address_at(*pointer_address);
|
|
|
|
}
|
|
|
|
bool change = *above_frame_address != *pointer_address;
|
|
|
|
*above_frame_address = *pointer_address;
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-28 10:21:46 +00:00
|
|
|
// Initializes an artificial stack frame. The data it contains is used for:
|
|
|
|
// a. successful work of frame dropper code which eventually gets control,
|
|
|
|
// b. being compatible with regular stack structure for various stack
|
|
|
|
// iterators.
|
|
|
|
// Returns address of stack allocated pointer to restarted function,
|
|
|
|
// the value that is called 'restarter_frame_function_pointer'. The value
|
|
|
|
// at this address (possibly updated by GC) may be used later when preparing
|
|
|
|
// 'step in' operation.
|
|
|
|
// Frame structure (conforms InternalFrame structure):
|
|
|
|
// -- code
|
|
|
|
// -- SMI maker
|
|
|
|
// -- function (slot is called "context")
|
|
|
|
// -- frame base
|
|
|
|
static Object** SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
|
|
|
|
Handle<Code> code) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(bottom_js_frame->is_java_script());
|
2014-05-28 10:21:46 +00:00
|
|
|
|
|
|
|
Address fp = bottom_js_frame->fp();
|
|
|
|
|
|
|
|
// Move function pointer into "context" slot.
|
|
|
|
Memory::Object_at(fp + StandardFrameConstants::kContextOffset) =
|
|
|
|
Memory::Object_at(fp + JavaScriptFrameConstants::kFunctionOffset);
|
|
|
|
|
|
|
|
Memory::Object_at(fp + InternalFrameConstants::kCodeOffset) = *code;
|
|
|
|
Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset) =
|
|
|
|
Smi::FromInt(StackFrame::INTERNAL);
|
|
|
|
|
|
|
|
return reinterpret_cast<Object**>(&Memory::Object_at(
|
|
|
|
fp + StandardFrameConstants::kContextOffset));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-06 17:58:28 +00:00
|
|
|
// Removes specified range of frames from stack. There may be 1 or more
|
|
|
|
// frames in range. Anyway the bottom frame is restarted rather than dropped,
|
|
|
|
// and therefore has to be a JavaScript frame.
|
|
|
|
// Returns error message or NULL.
|
|
|
|
static const char* DropFrames(Vector<StackFrame*> frames,
|
|
|
|
int top_frame_index,
|
2010-06-30 17:29:00 +00:00
|
|
|
int bottom_js_frame_index,
|
2014-05-28 10:21:46 +00:00
|
|
|
LiveEdit::FrameDropMode* mode,
|
2010-07-30 11:58:43 +00:00
|
|
|
Object*** restarter_frame_function_pointer) {
|
2014-05-28 10:21:46 +00:00
|
|
|
if (!LiveEdit::kFrameDropperSupported) {
|
2010-06-30 17:29:00 +00:00
|
|
|
return "Stack manipulations are not supported in this architecture.";
|
|
|
|
}
|
|
|
|
|
2010-04-06 17:58:28 +00:00
|
|
|
StackFrame* pre_top_frame = frames[top_frame_index - 1];
|
|
|
|
StackFrame* top_frame = frames[top_frame_index];
|
|
|
|
StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
|
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(bottom_js_frame->is_java_script());
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
// Check the nature of the top frame.
|
2013-09-04 10:34:42 +00:00
|
|
|
Isolate* isolate = bottom_js_frame->isolate();
|
Simplify isolates access during stack iteration (WAS: Move SafeStackFrameIterator::active_count_...)
While trying to fix Mac and Windows versions for this change:
http://codereview.chromium.org/6771047/, I figured out, that we
already store an isolate in StackFrameIterator, so we can use it in
frame objects, instead of requiring it from caller.
I've changed iterators usage to the following scheme: whenever a
caller maintains an isolate pointer, it just passes it to stack
iterator, and no more worries about passing it to frame content
accessors. If a caller uses current isolate, it can omit passing it
to iterator, in this case, an iterator will use the current isolate,
too.
There was a special case with LiveEdit, which creates
detached copies of frame objects.
R=vitalyr@chromium.org
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6794019
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7499 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2011-04-05 09:01:47 +00:00
|
|
|
Code* pre_top_frame_code = pre_top_frame->LookupCode();
|
2014-05-28 10:21:46 +00:00
|
|
|
bool frame_has_padding = true;
|
2011-03-18 20:35:07 +00:00
|
|
|
if (pre_top_frame_code->is_inline_cache_stub() &&
|
2013-08-22 12:16:00 +00:00
|
|
|
pre_top_frame_code->is_debug_stub()) {
|
2010-04-06 17:58:28 +00:00
|
|
|
// OK, we can drop inline cache calls.
|
2014-05-28 10:21:46 +00:00
|
|
|
*mode = LiveEdit::FRAME_DROPPED_IN_IC_CALL;
|
2011-03-18 20:35:07 +00:00
|
|
|
} else if (pre_top_frame_code ==
|
2014-05-05 07:10:38 +00:00
|
|
|
isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) {
|
2010-06-30 17:29:00 +00:00
|
|
|
// OK, we can drop debug break slot.
|
2014-05-28 10:21:46 +00:00
|
|
|
*mode = LiveEdit::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
|
2011-03-18 20:35:07 +00:00
|
|
|
} else if (pre_top_frame_code ==
|
2014-05-28 10:21:46 +00:00
|
|
|
isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) {
|
2010-04-06 17:58:28 +00:00
|
|
|
// OK, we can drop our own code.
|
2012-06-22 20:50:03 +00:00
|
|
|
pre_top_frame = frames[top_frame_index - 2];
|
|
|
|
top_frame = frames[top_frame_index - 1];
|
2014-05-28 10:21:46 +00:00
|
|
|
*mode = LiveEdit::CURRENTLY_SET_MODE;
|
2012-05-03 17:31:34 +00:00
|
|
|
frame_has_padding = false;
|
2011-04-28 20:05:50 +00:00
|
|
|
} else if (pre_top_frame_code ==
|
2014-05-28 10:21:46 +00:00
|
|
|
isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
|
|
|
|
*mode = LiveEdit::FRAME_DROPPED_IN_RETURN_CALL;
|
2011-03-18 20:35:07 +00:00
|
|
|
} else if (pre_top_frame_code->kind() == Code::STUB &&
|
2014-07-21 13:10:14 +00:00
|
|
|
CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) {
|
2012-05-03 17:31:34 +00:00
|
|
|
// Entry from our unit tests on 'debugger' statement.
|
|
|
|
// It's fine, we support this case.
|
2014-05-28 10:21:46 +00:00
|
|
|
*mode = LiveEdit::FRAME_DROPPED_IN_DIRECT_CALL;
|
2012-05-03 17:31:34 +00:00
|
|
|
// We don't have a padding from 'debugger' statement call.
|
|
|
|
// Here the stub is CEntry, it's not debug-only and can't be padded.
|
|
|
|
// If anyone would complain, a proxy padded stub could be added.
|
|
|
|
frame_has_padding = false;
|
2012-06-22 20:50:03 +00:00
|
|
|
} else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
|
|
|
|
// This must be adaptor that remain from the frame dropping that
|
|
|
|
// is still on stack. A frame dropper frame must be above it.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(frames[top_frame_index - 2]->LookupCode() ==
|
2014-05-28 10:21:46 +00:00
|
|
|
isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
|
2012-06-22 20:50:03 +00:00
|
|
|
pre_top_frame = frames[top_frame_index - 3];
|
|
|
|
top_frame = frames[top_frame_index - 2];
|
2014-05-28 10:21:46 +00:00
|
|
|
*mode = LiveEdit::CURRENTLY_SET_MODE;
|
2012-06-22 20:50:03 +00:00
|
|
|
frame_has_padding = false;
|
2010-04-06 17:58:28 +00:00
|
|
|
} else {
|
|
|
|
return "Unknown structure of stack above changing function";
|
|
|
|
}
|
|
|
|
|
|
|
|
Address unused_stack_top = top_frame->sp();
|
2014-05-28 10:21:46 +00:00
|
|
|
int new_frame_size = LiveEdit::kFrameDropperFrameSize * kPointerSize;
|
2010-04-06 17:58:28 +00:00
|
|
|
Address unused_stack_bottom = bottom_js_frame->fp()
|
2014-05-28 10:21:46 +00:00
|
|
|
- new_frame_size + kPointerSize; // Bigger address end is exclusive.
|
2010-04-06 17:58:28 +00:00
|
|
|
|
2012-05-03 17:31:34 +00:00
|
|
|
Address* top_frame_pc_address = top_frame->pc_address();
|
|
|
|
|
|
|
|
// top_frame may be damaged below this point. Do not used it.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(!(top_frame = NULL));
|
2012-05-03 17:31:34 +00:00
|
|
|
|
2010-04-06 17:58:28 +00:00
|
|
|
if (unused_stack_top > unused_stack_bottom) {
|
2012-05-03 17:31:34 +00:00
|
|
|
if (frame_has_padding) {
|
2012-05-03 22:19:12 +00:00
|
|
|
int shortage_bytes =
|
|
|
|
static_cast<int>(unused_stack_top - unused_stack_bottom);
|
2012-05-03 17:31:34 +00:00
|
|
|
|
|
|
|
Address padding_start = pre_top_frame->fp() -
|
2014-05-28 10:21:46 +00:00
|
|
|
LiveEdit::kFrameDropperFrameSize * kPointerSize;
|
2012-05-03 17:31:34 +00:00
|
|
|
|
|
|
|
Address padding_pointer = padding_start;
|
2014-05-28 10:21:46 +00:00
|
|
|
Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue);
|
2012-05-03 17:31:34 +00:00
|
|
|
while (Memory::Object_at(padding_pointer) == padding_object) {
|
|
|
|
padding_pointer -= kPointerSize;
|
|
|
|
}
|
|
|
|
int padding_counter =
|
|
|
|
Smi::cast(Memory::Object_at(padding_pointer))->value();
|
|
|
|
if (padding_counter * kPointerSize < shortage_bytes) {
|
|
|
|
return "Not enough space for frame dropper frame "
|
|
|
|
"(even with padding frame)";
|
|
|
|
}
|
|
|
|
Memory::Object_at(padding_pointer) =
|
|
|
|
Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
|
|
|
|
|
|
|
|
StackFrame* pre_pre_frame = frames[top_frame_index - 2];
|
|
|
|
|
2014-05-27 07:57:22 +00:00
|
|
|
MemMove(padding_start + kPointerSize - shortage_bytes,
|
|
|
|
padding_start + kPointerSize,
|
2014-05-28 10:21:46 +00:00
|
|
|
LiveEdit::kFrameDropperFrameSize * kPointerSize);
|
2012-05-03 17:31:34 +00:00
|
|
|
|
|
|
|
pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
|
|
|
|
pre_pre_frame->SetCallerFp(pre_top_frame->fp());
|
|
|
|
unused_stack_top -= shortage_bytes;
|
|
|
|
|
|
|
|
STATIC_ASSERT(sizeof(Address) == kPointerSize);
|
|
|
|
top_frame_pc_address -= shortage_bytes / kPointerSize;
|
|
|
|
} else {
|
|
|
|
return "Not enough space for frame dropper frame";
|
|
|
|
}
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Committing now. After this point we should return only NULL value.
|
|
|
|
|
|
|
|
FixTryCatchHandler(pre_top_frame, bottom_js_frame);
|
|
|
|
// Make sure FixTryCatchHandler is idempotent.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
|
2010-04-06 17:58:28 +00:00
|
|
|
|
2013-09-04 10:34:42 +00:00
|
|
|
Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit();
|
2012-05-03 17:31:34 +00:00
|
|
|
*top_frame_pc_address = code->entry();
|
2010-04-06 17:58:28 +00:00
|
|
|
pre_top_frame->SetCallerFp(bottom_js_frame->fp());
|
|
|
|
|
2010-07-30 11:58:43 +00:00
|
|
|
*restarter_frame_function_pointer =
|
2014-05-28 10:21:46 +00:00
|
|
|
SetUpFrameDropperFrame(bottom_js_frame, code);
|
2010-07-30 11:58:43 +00:00
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK((**restarter_frame_function_pointer)->IsJSFunction());
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
for (Address a = unused_stack_top;
|
|
|
|
a < unused_stack_bottom;
|
|
|
|
a += kPointerSize) {
|
|
|
|
Memory::Object_at(a) = Smi::FromInt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-15 16:52:03 +00:00
|
|
|
// Describes a set of call frames that execute any of listed functions.
|
|
|
|
// Finding no such frames does not mean error.
|
|
|
|
class MultipleFunctionTarget {
|
|
|
|
public:
|
|
|
|
MultipleFunctionTarget(Handle<JSArray> shared_info_array,
|
|
|
|
Handle<JSArray> result)
|
|
|
|
: m_shared_info_array(shared_info_array),
|
|
|
|
m_result(result) {}
|
|
|
|
bool MatchActivation(StackFrame* frame,
|
|
|
|
LiveEdit::FunctionPatchabilityStatus status) {
|
|
|
|
return CheckActivation(m_shared_info_array, m_result, frame, status);
|
|
|
|
}
|
2014-06-03 15:45:38 +00:00
|
|
|
const char* GetNotFoundMessage() const {
|
2012-06-15 16:52:03 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
Handle<JSArray> m_shared_info_array;
|
|
|
|
Handle<JSArray> m_result;
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2012-06-15 16:52:03 +00:00
|
|
|
// Drops all call frame matched by target and all frames above them.
|
|
|
|
template<typename TARGET>
|
|
|
|
static const char* DropActivationsInActiveThreadImpl(
|
2014-06-03 15:45:38 +00:00
|
|
|
Isolate* isolate,
|
|
|
|
TARGET& target, // NOLINT
|
|
|
|
bool do_drop) {
|
2011-05-23 22:23:50 +00:00
|
|
|
Debug* debug = isolate->debug();
|
2013-06-26 13:36:16 +00:00
|
|
|
Zone zone(isolate);
|
|
|
|
Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
int top_frame_index = -1;
|
|
|
|
int frame_index = 0;
|
|
|
|
for (; frame_index < frames.length(); frame_index++) {
|
|
|
|
StackFrame* frame = frames[frame_index];
|
2011-03-18 20:35:07 +00:00
|
|
|
if (frame->id() == debug->break_frame_id()) {
|
2010-04-06 17:58:28 +00:00
|
|
|
top_frame_index = frame_index;
|
|
|
|
break;
|
|
|
|
}
|
2012-06-15 16:52:03 +00:00
|
|
|
if (target.MatchActivation(
|
|
|
|
frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
|
2010-04-06 17:58:28 +00:00
|
|
|
// We are still above break_frame. It is not a target frame,
|
|
|
|
// it is a problem.
|
|
|
|
return "Debugger mark-up on stack is not found";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top_frame_index == -1) {
|
|
|
|
// We haven't found break frame, but no function is blocking us anyway.
|
2012-06-15 16:52:03 +00:00
|
|
|
return target.GetNotFoundMessage();
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool target_frame_found = false;
|
|
|
|
int bottom_js_frame_index = top_frame_index;
|
2014-05-22 07:32:59 +00:00
|
|
|
bool non_droppable_frame_found = false;
|
|
|
|
LiveEdit::FunctionPatchabilityStatus non_droppable_reason;
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
for (; frame_index < frames.length(); frame_index++) {
|
|
|
|
StackFrame* frame = frames[frame_index];
|
2014-05-22 07:32:59 +00:00
|
|
|
if (frame->is_exit()) {
|
|
|
|
non_droppable_frame_found = true;
|
|
|
|
non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (frame->is_java_script() &&
|
|
|
|
JavaScriptFrame::cast(frame)->function()->shared()->is_generator()) {
|
|
|
|
non_droppable_frame_found = true;
|
|
|
|
non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
|
2010-04-06 17:58:28 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-06-15 16:52:03 +00:00
|
|
|
if (target.MatchActivation(
|
|
|
|
frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
|
2010-04-06 17:58:28 +00:00
|
|
|
target_frame_found = true;
|
|
|
|
bottom_js_frame_index = frame_index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 07:32:59 +00:00
|
|
|
if (non_droppable_frame_found) {
|
|
|
|
// There is a C or generator frame on stack. We can't drop C frames, and we
|
|
|
|
// can't restart generators. Check that there are no target frames below
|
|
|
|
// them.
|
2010-04-06 17:58:28 +00:00
|
|
|
for (; frame_index < frames.length(); frame_index++) {
|
|
|
|
StackFrame* frame = frames[frame_index];
|
|
|
|
if (frame->is_java_script()) {
|
2014-05-22 07:32:59 +00:00
|
|
|
if (target.MatchActivation(frame, non_droppable_reason)) {
|
|
|
|
// Fail.
|
2010-04-06 17:58:28 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!do_drop) {
|
|
|
|
// We are in check-only mode.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!target_frame_found) {
|
|
|
|
// Nothing to drop.
|
2012-06-15 16:52:03 +00:00
|
|
|
return target.GetNotFoundMessage();
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 10:21:46 +00:00
|
|
|
LiveEdit::FrameDropMode drop_mode = LiveEdit::FRAMES_UNTOUCHED;
|
2010-07-30 11:58:43 +00:00
|
|
|
Object** restarter_frame_function_pointer = NULL;
|
2010-04-06 17:58:28 +00:00
|
|
|
const char* error_message = DropFrames(frames, top_frame_index,
|
2010-07-30 11:58:43 +00:00
|
|
|
bottom_js_frame_index, &drop_mode,
|
|
|
|
&restarter_frame_function_pointer);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
if (error_message != NULL) {
|
|
|
|
return error_message;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust break_frame after some frames has been dropped.
|
|
|
|
StackFrame::Id new_id = StackFrame::NO_ID;
|
|
|
|
for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
|
|
|
|
if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
|
|
|
|
new_id = frames[i]->id();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-06-02 12:51:19 +00:00
|
|
|
debug->FramesHaveBeenDropped(
|
|
|
|
new_id, drop_mode, restarter_frame_function_pointer);
|
2012-06-15 16:52:03 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2012-06-15 16:52:03 +00:00
|
|
|
// Fills result array with statuses of functions. Modifies the stack
|
|
|
|
// removing all listed function if possible and if do_drop is true.
|
|
|
|
static const char* DropActivationsInActiveThread(
|
2013-06-26 13:36:16 +00:00
|
|
|
Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
|
2012-06-15 16:52:03 +00:00
|
|
|
MultipleFunctionTarget target(shared_info_array, result);
|
|
|
|
|
2013-09-04 10:34:42 +00:00
|
|
|
const char* message = DropActivationsInActiveThreadImpl(
|
|
|
|
shared_info_array->GetIsolate(), target, do_drop);
|
2012-06-15 16:52:03 +00:00
|
|
|
if (message) {
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate = shared_info_array->GetIsolate();
|
2012-09-14 13:31:11 +00:00
|
|
|
int array_len = GetArrayLength(shared_info_array);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
// Replace "blocked on active" with "replaced on active" status.
|
|
|
|
for (int i = 0; i < array_len; i++) {
|
2014-03-20 12:22:13 +00:00
|
|
|
Handle<Object> obj =
|
2014-04-11 12:47:34 +00:00
|
|
|
Object::GetElement(isolate, result, i).ToHandleChecked();
|
2014-03-18 12:34:02 +00:00
|
|
|
if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
|
2010-08-31 08:05:42 +00:00
|
|
|
Handle<Object> replaced(
|
2013-02-25 14:46:09 +00:00
|
|
|
Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(result, i, replaced);
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-22 07:32:59 +00:00
|
|
|
bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
|
|
|
|
Handle<FixedArray> result,
|
|
|
|
int len) {
|
|
|
|
Isolate* isolate = shared_info_array->GetIsolate();
|
|
|
|
bool found_suspended_activations = false;
|
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_LE(len, result->length());
|
2014-05-22 07:32:59 +00:00
|
|
|
|
|
|
|
FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR;
|
|
|
|
|
2014-05-22 11:13:37 +00:00
|
|
|
Heap* heap = isolate->heap();
|
2014-05-22 07:32:59 +00:00
|
|
|
HeapIterator iterator(heap);
|
|
|
|
HeapObject* obj = NULL;
|
|
|
|
while ((obj = iterator.next()) != NULL) {
|
|
|
|
if (!obj->IsJSGeneratorObject()) continue;
|
|
|
|
|
|
|
|
JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
|
|
|
|
if (gen->is_closed()) continue;
|
|
|
|
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
Handle<JSValue> jsvalue =
|
|
|
|
Handle<JSValue>::cast(FixedArray::get(shared_info_array, i));
|
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
UnwrapSharedFunctionInfoFromJSValue(jsvalue);
|
|
|
|
|
|
|
|
if (gen->function()->shared() == *shared) {
|
|
|
|
result->set(i, Smi::FromInt(active));
|
|
|
|
found_suspended_activations = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found_suspended_activations;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-06 17:58:28 +00:00
|
|
|
class InactiveThreadActivationsChecker : public ThreadVisitor {
|
|
|
|
public:
|
|
|
|
InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
|
|
|
|
Handle<JSArray> result)
|
|
|
|
: shared_info_array_(shared_info_array), result_(result),
|
|
|
|
has_blocked_functions_(false) {
|
|
|
|
}
|
Simplify isolates access during stack iteration (WAS: Move SafeStackFrameIterator::active_count_...)
While trying to fix Mac and Windows versions for this change:
http://codereview.chromium.org/6771047/, I figured out, that we
already store an isolate in StackFrameIterator, so we can use it in
frame objects, instead of requiring it from caller.
I've changed iterators usage to the following scheme: whenever a
caller maintains an isolate pointer, it just passes it to stack
iterator, and no more worries about passing it to frame content
accessors. If a caller uses current isolate, it can omit passing it
to iterator, in this case, an iterator will use the current isolate,
too.
There was a special case with LiveEdit, which creates
detached copies of frame objects.
R=vitalyr@chromium.org
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6794019
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7499 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2011-04-05 09:01:47 +00:00
|
|
|
void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
|
|
|
|
for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
|
2010-04-06 17:58:28 +00:00
|
|
|
has_blocked_functions_ |= CheckActivation(
|
|
|
|
shared_info_array_, result_, it.frame(),
|
|
|
|
LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool HasBlockedFunctions() {
|
|
|
|
return has_blocked_functions_;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<JSArray> shared_info_array_;
|
|
|
|
Handle<JSArray> result_;
|
|
|
|
bool has_blocked_functions_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Handle<JSArray> LiveEdit::CheckAndDropActivations(
|
2013-06-26 13:36:16 +00:00
|
|
|
Handle<JSArray> shared_info_array, bool do_drop) {
|
2013-02-25 14:46:09 +00:00
|
|
|
Isolate* isolate = shared_info_array->GetIsolate();
|
2012-09-14 13:31:11 +00:00
|
|
|
int len = GetArrayLength(shared_info_array);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(shared_info_array->HasFastElements());
|
2014-05-22 07:32:59 +00:00
|
|
|
Handle<FixedArray> shared_info_array_elements(
|
|
|
|
FixedArray::cast(shared_info_array->elements()));
|
|
|
|
|
2013-02-25 14:46:09 +00:00
|
|
|
Handle<JSArray> result = isolate->factory()->NewJSArray(len);
|
2014-05-22 07:32:59 +00:00
|
|
|
Handle<FixedArray> result_elements =
|
|
|
|
JSObject::EnsureWritableFastElements(result);
|
2010-04-06 17:58:28 +00:00
|
|
|
|
|
|
|
// Fill the default values.
|
|
|
|
for (int i = 0; i < len; i++) {
|
2014-05-22 07:32:59 +00:00
|
|
|
FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH;
|
|
|
|
result_elements->set(i, Smi::FromInt(status));
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
|
2014-05-22 07:32:59 +00:00
|
|
|
// Scan the heap for active generators -- those that are either currently
|
|
|
|
// running (as we wouldn't want to restart them, because we don't know where
|
|
|
|
// to restart them from) or suspended. Fail if any one corresponds to the set
|
|
|
|
// of functions being edited.
|
|
|
|
if (FindActiveGenerators(shared_info_array_elements, result_elements, len)) {
|
|
|
|
return result;
|
|
|
|
}
|
2010-04-06 17:58:28 +00:00
|
|
|
|
2014-05-22 07:32:59 +00:00
|
|
|
// Check inactive threads. Fail if some functions are blocked there.
|
2010-04-06 17:58:28 +00:00
|
|
|
InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
|
|
|
|
result);
|
2013-09-04 10:34:42 +00:00
|
|
|
isolate->thread_manager()->IterateArchivedThreads(
|
2011-03-18 20:35:07 +00:00
|
|
|
&inactive_threads_checker);
|
2010-04-06 17:58:28 +00:00
|
|
|
if (inactive_threads_checker.HasBlockedFunctions()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to drop activations from the current stack.
|
|
|
|
const char* error_message =
|
2013-06-26 13:36:16 +00:00
|
|
|
DropActivationsInActiveThread(shared_info_array, result, do_drop);
|
2010-04-06 17:58:28 +00:00
|
|
|
if (error_message != NULL) {
|
|
|
|
// Add error message as an array extra element.
|
2014-04-17 13:27:02 +00:00
|
|
|
Handle<String> str =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(error_message);
|
2014-03-11 14:39:08 +00:00
|
|
|
SetElementSloppy(result, len, str);
|
2010-04-06 17:58:28 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-15 16:52:03 +00:00
|
|
|
// Describes a single callframe a target. Not finding this frame
|
|
|
|
// means an error.
|
|
|
|
class SingleFrameTarget {
|
|
|
|
public:
|
2012-07-13 09:23:46 +00:00
|
|
|
explicit SingleFrameTarget(JavaScriptFrame* frame)
|
|
|
|
: m_frame(frame),
|
|
|
|
m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
|
2012-06-15 16:52:03 +00:00
|
|
|
|
|
|
|
bool MatchActivation(StackFrame* frame,
|
|
|
|
LiveEdit::FunctionPatchabilityStatus status) {
|
|
|
|
if (frame->fp() == m_frame->fp()) {
|
|
|
|
m_saved_status = status;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-06-03 15:45:38 +00:00
|
|
|
const char* GetNotFoundMessage() const {
|
2012-06-15 16:52:03 +00:00
|
|
|
return "Failed to found requested frame";
|
|
|
|
}
|
|
|
|
LiveEdit::FunctionPatchabilityStatus saved_status() {
|
|
|
|
return m_saved_status;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
JavaScriptFrame* m_frame;
|
|
|
|
LiveEdit::FunctionPatchabilityStatus m_saved_status;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Finds a drops required frame and all frames above.
|
|
|
|
// Returns error message or NULL.
|
2013-06-26 13:36:16 +00:00
|
|
|
const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
|
2012-06-15 16:52:03 +00:00
|
|
|
SingleFrameTarget target(frame);
|
|
|
|
|
2013-09-04 10:34:42 +00:00
|
|
|
const char* result = DropActivationsInActiveThreadImpl(
|
|
|
|
frame->isolate(), target, true);
|
2012-07-13 09:02:03 +00:00
|
|
|
if (result != NULL) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
|
|
|
|
return "Function is blocked under native code";
|
|
|
|
}
|
2014-05-22 07:32:59 +00:00
|
|
|
if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) {
|
|
|
|
return "Function is blocked under a generator activation";
|
|
|
|
}
|
2012-07-13 09:02:03 +00:00
|
|
|
return NULL;
|
2012-06-15 16:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-18 20:35:07 +00:00
|
|
|
LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
|
|
|
|
FunctionLiteral* fun)
|
|
|
|
: isolate_(isolate) {
|
|
|
|
if (isolate_->active_function_info_listener() != NULL) {
|
|
|
|
isolate_->active_function_info_listener()->FunctionStarted(fun);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LiveEditFunctionTracker::~LiveEditFunctionTracker() {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (isolate_->active_function_info_listener() != NULL) {
|
|
|
|
isolate_->active_function_info_listener()->FunctionDone();
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
void LiveEditFunctionTracker::RecordFunctionInfo(
|
2012-06-11 12:42:31 +00:00
|
|
|
Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
|
|
|
|
Zone* zone) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (isolate_->active_function_info_listener() != NULL) {
|
2012-06-11 12:42:31 +00:00
|
|
|
isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(),
|
|
|
|
zone);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-28 11:38:43 +00:00
|
|
|
void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
|
2011-03-18 20:35:07 +00:00
|
|
|
isolate_->active_function_info_listener()->FunctionCode(code);
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-18 20:35:07 +00:00
|
|
|
bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
|
|
|
|
return isolate->active_function_info_listener() != NULL;
|
2010-03-05 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
2010-02-17 20:37:08 +00:00
|
|
|
} } // namespace v8::internal
|