Commit ad876956 authored by peter.rybin@gmail.com's avatar peter.rybin@gmail.com

LiveEdit: patch positions in function

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4139 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ed532680
...@@ -560,6 +560,11 @@ Handle<Code> Factory::CopyCode(Handle<Code> code) { ...@@ -560,6 +560,11 @@ Handle<Code> Factory::CopyCode(Handle<Code> code) {
} }
Handle<Code> Factory::CopyCode(Handle<Code> code, Vector<byte> reloc_info) {
CALL_HEAP_FUNCTION(Heap::CopyCode(*code, reloc_info), Code);
}
static inline Object* DoCopyInsert(DescriptorArray* array, static inline Object* DoCopyInsert(DescriptorArray* array,
String* key, String* key,
Object* value, Object* value,
......
...@@ -230,6 +230,8 @@ class Factory : public AllStatic { ...@@ -230,6 +230,8 @@ class Factory : public AllStatic {
static Handle<Code> CopyCode(Handle<Code> code); static Handle<Code> CopyCode(Handle<Code> code);
static Handle<Code> CopyCode(Handle<Code> code, Vector<byte> reloc_info);
static Handle<Object> ToObject(Handle<Object> object); static Handle<Object> ToObject(Handle<Object> object);
static Handle<Object> ToObject(Handle<Object> object, static Handle<Object> ToObject(Handle<Object> object,
Handle<Context> global_context); Handle<Context> global_context);
......
...@@ -2257,6 +2257,55 @@ Object* Heap::CopyCode(Code* code) { ...@@ -2257,6 +2257,55 @@ Object* Heap::CopyCode(Code* code) {
} }
Object* Heap::CopyCode(Code* code, Vector<byte> reloc_info) {
int new_body_size = RoundUp(code->instruction_size() + reloc_info.length(),
kObjectAlignment);
int sinfo_size = code->sinfo_size();
int new_obj_size = Code::SizeFor(new_body_size, sinfo_size);
Address old_addr = code->address();
int relocation_offset = code->relocation_start() - old_addr;
Object* result;
if (new_obj_size > MaxObjectSizeInPagedSpace()) {
result = lo_space_->AllocateRawCode(new_obj_size);
} else {
result = code_space_->AllocateRaw(new_obj_size);
}
if (result->IsFailure()) return result;
// Copy code object.
Address new_addr = reinterpret_cast<HeapObject*>(result)->address();
// Copy header and instructions.
memcpy(new_addr, old_addr, relocation_offset);
// Copy patched rinfo.
memcpy(new_addr + relocation_offset,
reloc_info.start(),
reloc_info.length());
Code* new_code = Code::cast(result);
new_code->set_relocation_size(reloc_info.length());
// Copy sinfo.
memcpy(new_code->sinfo_start(), code->sinfo_start(), code->sinfo_size());
// Relocate the copy.
ASSERT(!CodeRange::exists() || CodeRange::contains(code->address()));
new_code->Relocate(new_addr - old_addr);
#ifdef DEBUG
code->Verify();
#endif
return new_code;
}
Object* Heap::Allocate(Map* map, AllocationSpace space) { Object* Heap::Allocate(Map* map, AllocationSpace space) {
ASSERT(gc_state_ == NOT_IN_GC); ASSERT(gc_state_ == NOT_IN_GC);
ASSERT(map->instance_type() != MAP_TYPE); ASSERT(map->instance_type() != MAP_TYPE);
......
...@@ -612,6 +612,11 @@ class Heap : public AllStatic { ...@@ -612,6 +612,11 @@ class Heap : public AllStatic {
Handle<Object> self_reference); Handle<Object> self_reference);
static Object* CopyCode(Code* code); static Object* CopyCode(Code* code);
// Copy the code and scope info part of the code object, but insert
// the provided data as the relocation information.
static Object* CopyCode(Code* code, Vector<byte> reloc_info);
// Finds the symbol for string in the symbol table. // Finds the symbol for string in the symbol table.
// If not found, a new symbol is added to the table and returned. // If not found, a new symbol is added to the table and returned.
// Returns Failure::RetryAfterGC(requested_bytes, space) if allocation // Returns Failure::RetryAfterGC(requested_bytes, space) if allocation
......
...@@ -424,3 +424,8 @@ Debug.LiveEditChangeScript.Failure = function(message) { ...@@ -424,3 +424,8 @@ Debug.LiveEditChangeScript.Failure = function(message) {
Debug.LiveEditChangeScript.Failure.prototype.toString = function() { Debug.LiveEditChangeScript.Failure.prototype.toString = function() {
return "LiveEdit Failure: " + this.message; return "LiveEdit Failure: " + this.message;
} }
// A testing entry.
Debug.LiveEditChangeScript.GetPcFromSourcePos = function(func, source_pos) {
return %GetFunctionCodePositionFromSource(func, source_pos);
}
This diff is collapsed.
...@@ -9063,6 +9063,36 @@ static Object* Runtime_LiveEditCheckStackActivations(Arguments args) { ...@@ -9063,6 +9063,36 @@ static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
} }
// A testing entry. Returns statement position which is the closest to
// source_position.
static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
ASSERT(args.length() == 2);
HandleScope scope;
CONVERT_ARG_CHECKED(JSFunction, function, 0);
CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
Handle<Code> code(function->code());
RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
int closest_pc = 0;
int distance = kMaxInt;
while (!it.done()) {
int statement_position = static_cast<int>(it.rinfo()->data());
// Check if this break point is closer that what was previously found.
if (source_position <= statement_position &&
statement_position - source_position < distance) {
closest_pc = it.rinfo()->pc() - code->instruction_start();
distance = statement_position - source_position;
// Check whether we can't get any closer.
if (distance == 0) break;
}
it.next();
}
return Smi::FromInt(closest_pc);
}
#endif // ENABLE_DEBUGGER_SUPPORT #endif // ENABLE_DEBUGGER_SUPPORT
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
......
...@@ -333,7 +333,8 @@ namespace internal { ...@@ -333,7 +333,8 @@ namespace internal {
F(LiveEditReplaceFunctionCode, 2, 1) \ F(LiveEditReplaceFunctionCode, 2, 1) \
F(LiveEditRelinkFunctionToScript, 2, 1) \ F(LiveEditRelinkFunctionToScript, 2, 1) \
F(LiveEditPatchFunctionPositions, 2, 1) \ F(LiveEditPatchFunctionPositions, 2, 1) \
F(LiveEditCheckStackActivations, 1, 1) F(LiveEditCheckStackActivations, 1, 1) \
F(GetFunctionCodePositionFromSource, 2, 1)
#else #else
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) #define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
#endif #endif
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Get the Debug object exposed from the debug context global object.
// Scenario: a function is being changed, which causes enclosing function to
// have its positions patched; position changing requires new instance of Code
// object to be introduced; the function happens to be on stack at this moment;
// later it will resume over new instance of Code.
// Before the change 2 rinfo are 22 characters away from each other. After the
// change they are 114 characters away from each other. New instance of Code is
// required when those numbers cross the border value of 64 (in any direction).
Debug = debug.Debug
eval(
"function BeingReplaced(changer, opt_x, opt_y) {\n" +
" changer();\n" +
" var res = new Object();\n" +
" if (opt_x) { res.y = opt_y; }\n" +
" res.a = (function() {})();\n" +
" return res.a;\n" +
"}"
);
var script = Debug.findScript(BeingReplaced);
var orig_body = "{}";
var patch_pos = script.source.indexOf(orig_body);
// Line long enough to change rinfo encoding.
var new_body_patch = "{return 'Capybara';" +
" " +
"}";
var change_log = new Array();
function Changer() {
Debug.LiveEditChangeScript(script, patch_pos, orig_body.length, new_body_patch, change_log);
print("Change log: " + JSON.stringify(change_log) + "\n");
}
function NoOp() {
}
function CallM(changer) {
// We expect call IC here after several function runs.
return BeingReplaced(changer);
}
// This several iterations should cause call IC for BeingReplaced call. This IC
// will keep reference to code object of BeingRepalced function. This reference
// should also be patched. Unfortunately, this is a manually checked fact (from
// debugger or debug print) and doesn't work as an automatic test.
CallM(NoOp);
CallM(NoOp);
CallM(NoOp);
var res = CallM(Changer);
assertEquals("Capybara", res);
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Get the Debug object exposed from the debug context global object.
// Scenario: some function is being edited; the outer function has to have its
// positions patched. Accoring to a special markup of function text
// corresponding byte-code PCs should conicide before change and after it.
Debug = debug.Debug
eval(
"function F1() { return 5; }\n" +
"function ChooseAnimal(/*$*/ ) {\n" +
"/*$*/ var x = F1(/*$*/ );\n" +
"/*$*/ var res/*$*/ =/*$*/ (function() { return 'Cat'; } )();\n" +
"/*$*/ var y/*$*/ = F2(/*$*/ F1()/*$*/ , F1(/*$*/ )/*$*/ );\n" +
"/*$*/ if (/*$*/ x.toString(/*$*/ )) { /*$*/ y = 3;/*$*/ } else {/*$*/ y = 8;/*$*/ }\n" +
"/*$*/ var z = /*$*/ x * y;\n" +
"/*$*/ return/*$*/ res/*$*/ + z;/*$*/ }\n" +
"function F2(x, y) { return x + y; }"
);
// Find all *$* markers in text of the function and read corresponding statement
// PCs.
function ReadMarkerPositions(func) {
var text = func.toString();
var positions = new Array();
var match;
var pattern = /\/\*\$\*\//g;
while ((match = pattern.exec(text)) != null) {
positions.push(match.index);
}
return positions;
}
function ReadPCMap(func, positions) {
var res = new Array();
for (var i = 0; i < positions.length; i++) {
res.push(Debug.LiveEditChangeScript.GetPcFromSourcePos(func, positions[i]));
}
return res;
}
var res = ChooseAnimal();
assertEquals("Cat15", res);
var markerPositionsBefore = ReadMarkerPositions(ChooseAnimal);
var pcArrayBefore = ReadPCMap(ChooseAnimal, markerPositionsBefore);
var script = Debug.findScript(ChooseAnimal);
var orig_animal = "'Cat'";
var patch_pos = script.source.indexOf(orig_animal);
var new_animal_patch = "'Capybara'";
var change_log = new Array();
Debug.LiveEditChangeScript(script, patch_pos, orig_animal.length, new_animal_patch, change_log);
print("Change log: " + JSON.stringify(change_log) + "\n");
var res = ChooseAnimal();
assertEquals("Capybara15", res);
var markerPositionsAfter = ReadMarkerPositions(ChooseAnimal);
var pcArrayAfter = ReadPCMap(ChooseAnimal, markerPositionsAfter);
assertArrayEquals(pcArrayBefore, pcArrayAfter);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment