mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
180 changed files with 16938 additions and 3555 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,268 @@ |
|||||
|
// Copyright 2009 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.
|
||||
|
|
||||
|
var $JSON = global.JSON; |
||||
|
|
||||
|
function ParseJSONUnfiltered(text) { |
||||
|
var s = $String(text); |
||||
|
var f = %CompileString(text, true); |
||||
|
return f(); |
||||
|
} |
||||
|
|
||||
|
function Revive(holder, name, reviver) { |
||||
|
var val = holder[name]; |
||||
|
if (IS_OBJECT(val)) { |
||||
|
if (IS_ARRAY(val)) { |
||||
|
var length = val.length; |
||||
|
for (var i = 0; i < length; i++) { |
||||
|
var newElement = Revive(val, $String(i), reviver); |
||||
|
val[i] = newElement; |
||||
|
} |
||||
|
} else { |
||||
|
for (var p in val) { |
||||
|
if (ObjectHasOwnProperty.call(val, p)) { |
||||
|
var newElement = Revive(val, p, reviver); |
||||
|
if (IS_UNDEFINED(newElement)) { |
||||
|
delete val[p]; |
||||
|
} else { |
||||
|
val[p] = newElement; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return reviver.call(holder, name, val); |
||||
|
} |
||||
|
|
||||
|
function JSONParse(text, reviver) { |
||||
|
var unfiltered = ParseJSONUnfiltered(text); |
||||
|
if (IS_FUNCTION(reviver)) { |
||||
|
return Revive({'': unfiltered}, '', reviver); |
||||
|
} else { |
||||
|
return unfiltered; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var characterQuoteCache = { |
||||
|
'\"': '\\"', |
||||
|
'\\': '\\\\', |
||||
|
'/': '\\/', |
||||
|
'\b': '\\b', |
||||
|
'\f': '\\f', |
||||
|
'\n': '\\n', |
||||
|
'\r': '\\r', |
||||
|
'\t': '\\t', |
||||
|
'\x0B': '\\u000b' |
||||
|
}; |
||||
|
|
||||
|
function QuoteSingleJSONCharacter(c) { |
||||
|
if (c in characterQuoteCache) { |
||||
|
return characterQuoteCache[c]; |
||||
|
} |
||||
|
var charCode = c.charCodeAt(0); |
||||
|
var result; |
||||
|
if (charCode < 16) result = '\\u000'; |
||||
|
else if (charCode < 256) result = '\\u00'; |
||||
|
else if (charCode < 4096) result = '\\u0'; |
||||
|
else result = '\\u'; |
||||
|
result += charCode.toString(16); |
||||
|
characterQuoteCache[c] = result; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
function QuoteJSONString(str) { |
||||
|
var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g; |
||||
|
return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"'; |
||||
|
} |
||||
|
|
||||
|
function StackContains(stack, val) { |
||||
|
var length = stack.length; |
||||
|
for (var i = 0; i < length; i++) { |
||||
|
if (stack[i] === val) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
function SerializeArray(value, replacer, stack, indent, gap) { |
||||
|
if (StackContains(stack, value)) { |
||||
|
throw MakeTypeError('circular_structure', []); |
||||
|
} |
||||
|
stack.push(value); |
||||
|
var stepback = indent; |
||||
|
indent += gap; |
||||
|
var partial = []; |
||||
|
var len = value.length; |
||||
|
for (var i = 0; i < len; i++) { |
||||
|
var strP = JSONSerialize($String(i), value, replacer, stack, |
||||
|
indent, gap); |
||||
|
if (IS_UNDEFINED(strP)) { |
||||
|
strP = "null"; |
||||
|
} |
||||
|
partial.push(strP); |
||||
|
} |
||||
|
var final; |
||||
|
if (gap == "") { |
||||
|
final = "[" + partial.join(",") + "]"; |
||||
|
} else if (partial.length > 0) { |
||||
|
var separator = ",\n" + indent; |
||||
|
final = "[\n" + indent + partial.join(separator) + "\n" + |
||||
|
stepback + "]"; |
||||
|
} else { |
||||
|
final = "[]"; |
||||
|
} |
||||
|
stack.pop(); |
||||
|
return final; |
||||
|
} |
||||
|
|
||||
|
function SerializeObject(value, replacer, stack, indent, gap) { |
||||
|
if (StackContains(stack, value)) { |
||||
|
throw MakeTypeError('circular_structure', []); |
||||
|
} |
||||
|
stack.push(value); |
||||
|
var stepback = indent; |
||||
|
indent += gap; |
||||
|
var partial = []; |
||||
|
if (IS_ARRAY(replacer)) { |
||||
|
var length = replacer.length; |
||||
|
for (var i = 0; i < length; i++) { |
||||
|
if (ObjectHasOwnProperty.call(replacer, i)) { |
||||
|
var p = replacer[i]; |
||||
|
var strP = JSONSerialize(p, value, replacer, stack, indent, gap); |
||||
|
if (!IS_UNDEFINED(strP)) { |
||||
|
var member = QuoteJSONString(p) + ":"; |
||||
|
if (gap != "") member += " "; |
||||
|
member += strP; |
||||
|
partial.push(member); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
for (var p in value) { |
||||
|
if (ObjectHasOwnProperty.call(value, p)) { |
||||
|
var strP = JSONSerialize(p, value, replacer, stack, indent, gap); |
||||
|
if (!IS_UNDEFINED(strP)) { |
||||
|
var member = QuoteJSONString(p) + ":"; |
||||
|
if (gap != "") member += " "; |
||||
|
member += strP; |
||||
|
partial.push(member); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
var final; |
||||
|
if (gap == "") { |
||||
|
final = "{" + partial.join(",") + "}"; |
||||
|
} else if (partial.length > 0) { |
||||
|
var separator = ",\n" + indent; |
||||
|
final = "{\n" + indent + partial.join(separator) + "\n" + |
||||
|
stepback + "}"; |
||||
|
} else { |
||||
|
final = "{}"; |
||||
|
} |
||||
|
stack.pop(); |
||||
|
return final; |
||||
|
} |
||||
|
|
||||
|
function JSONSerialize(key, holder, replacer, stack, indent, gap) { |
||||
|
var value = holder[key]; |
||||
|
if (IS_OBJECT(value) && value) { |
||||
|
var toJSON = value.toJSON; |
||||
|
if (IS_FUNCTION(toJSON)) { |
||||
|
value = toJSON.call(value, key); |
||||
|
} |
||||
|
} |
||||
|
if (IS_FUNCTION(replacer)) { |
||||
|
value = replacer.call(holder, key, value); |
||||
|
} |
||||
|
// Unwrap value if necessary
|
||||
|
if (IS_OBJECT(value)) { |
||||
|
if (IS_NUMBER_WRAPPER(value)) { |
||||
|
value = $Number(value); |
||||
|
} else if (IS_STRING_WRAPPER(value)) { |
||||
|
value = $String(value); |
||||
|
} else if (IS_BOOLEAN_WRAPPER(value)) { |
||||
|
value = $Boolean(value); |
||||
|
} |
||||
|
} |
||||
|
switch (typeof value) { |
||||
|
case "string": |
||||
|
return QuoteJSONString(value); |
||||
|
case "object": |
||||
|
if (!value) { |
||||
|
return "null"; |
||||
|
} else if (IS_ARRAY(value)) { |
||||
|
return SerializeArray(value, replacer, stack, indent, gap); |
||||
|
} else { |
||||
|
return SerializeObject(value, replacer, stack, indent, gap); |
||||
|
} |
||||
|
case "number": |
||||
|
return $isFinite(value) ? $String(value) : "null"; |
||||
|
case "boolean": |
||||
|
return value ? "true" : "false"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function JSONStringify(value, replacer, space) { |
||||
|
var stack = []; |
||||
|
var indent = ""; |
||||
|
if (IS_OBJECT(space)) { |
||||
|
// Unwrap 'space' if it is wrapped
|
||||
|
if (IS_NUMBER_WRAPPER(space)) { |
||||
|
space = $Number(space); |
||||
|
} else if (IS_STRING_WRAPPER(space)) { |
||||
|
space = $String(space); |
||||
|
} |
||||
|
} |
||||
|
var gap; |
||||
|
if (IS_NUMBER(space)) { |
||||
|
space = $Math.min(space, 10); |
||||
|
gap = ""; |
||||
|
for (var i = 0; i < space; i++) { |
||||
|
gap += " "; |
||||
|
} |
||||
|
} else if (IS_STRING(space)) { |
||||
|
if (space.length > 10) { |
||||
|
gap = space.substring(0, 10); |
||||
|
} else { |
||||
|
gap = space; |
||||
|
} |
||||
|
} else { |
||||
|
gap = ""; |
||||
|
} |
||||
|
return JSONSerialize('', {'': value}, replacer, stack, indent, gap); |
||||
|
} |
||||
|
|
||||
|
function SetupJSON() { |
||||
|
InstallFunctions($JSON, DONT_ENUM, $Array( |
||||
|
"parse", JSONParse, |
||||
|
"stringify", JSONStringify |
||||
|
)); |
||||
|
} |
||||
|
|
||||
|
SetupJSON(); |
@ -0,0 +1,51 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#ifndef V8_JUMP_TARGET_HEAVY_INL_H_ |
||||
|
#define V8_JUMP_TARGET_HEAVY_INL_H_ |
||||
|
|
||||
|
#include "virtual-frame-inl.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
void JumpTarget::InitializeEntryElement(int index, FrameElement* target) { |
||||
|
FrameElement* element = &entry_frame_->elements_[index]; |
||||
|
element->clear_copied(); |
||||
|
if (target->is_register()) { |
||||
|
entry_frame_->set_register_location(target->reg(), index); |
||||
|
} else if (target->is_copy()) { |
||||
|
entry_frame_->elements_[target->index()].set_copied(); |
||||
|
} |
||||
|
if (direction_ == BIDIRECTIONAL && !target->is_copy()) { |
||||
|
element->set_type_info(TypeInfo::Unknown()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_JUMP_TARGET_HEAVY_INL_H_
|
@ -0,0 +1,363 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#include "v8.h" |
||||
|
|
||||
|
#include "codegen-inl.h" |
||||
|
#include "jump-target-inl.h" |
||||
|
#include "register-allocator-inl.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Jump(Result* arg) { |
||||
|
ASSERT(cgen()->has_valid_frame()); |
||||
|
|
||||
|
cgen()->frame()->Push(arg); |
||||
|
DoJump(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) { |
||||
|
ASSERT(cgen()->has_valid_frame()); |
||||
|
|
||||
|
// We want to check that non-frame registers at the call site stay in
|
||||
|
// the same registers on the fall-through branch.
|
||||
|
#ifdef DEBUG |
||||
|
Result::Type arg_type = arg->type(); |
||||
|
Register arg_reg = arg->is_register() ? arg->reg() : no_reg; |
||||
|
#endif |
||||
|
|
||||
|
cgen()->frame()->Push(arg); |
||||
|
DoBranch(cc, hint); |
||||
|
*arg = cgen()->frame()->Pop(); |
||||
|
|
||||
|
ASSERT(arg->type() == arg_type); |
||||
|
ASSERT(!arg->is_register() || arg->reg().is(arg_reg)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) { |
||||
|
ASSERT(cgen()->has_valid_frame()); |
||||
|
|
||||
|
// We want to check that non-frame registers at the call site stay in
|
||||
|
// the same registers on the fall-through branch.
|
||||
|
#ifdef DEBUG |
||||
|
Result::Type arg0_type = arg0->type(); |
||||
|
Register arg0_reg = arg0->is_register() ? arg0->reg() : no_reg; |
||||
|
Result::Type arg1_type = arg1->type(); |
||||
|
Register arg1_reg = arg1->is_register() ? arg1->reg() : no_reg; |
||||
|
#endif |
||||
|
|
||||
|
cgen()->frame()->Push(arg0); |
||||
|
cgen()->frame()->Push(arg1); |
||||
|
DoBranch(cc, hint); |
||||
|
*arg1 = cgen()->frame()->Pop(); |
||||
|
*arg0 = cgen()->frame()->Pop(); |
||||
|
|
||||
|
ASSERT(arg0->type() == arg0_type); |
||||
|
ASSERT(!arg0->is_register() || arg0->reg().is(arg0_reg)); |
||||
|
ASSERT(arg1->type() == arg1_type); |
||||
|
ASSERT(!arg1->is_register() || arg1->reg().is(arg1_reg)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { |
||||
|
ASSERT(cgen()->has_valid_frame()); |
||||
|
|
||||
|
int count = cgen()->frame()->height() - expected_height_; |
||||
|
if (count > 0) { |
||||
|
// We negate and branch here rather than using DoBranch's negate
|
||||
|
// and branch. This gives us a hook to remove statement state
|
||||
|
// from the frame.
|
||||
|
JumpTarget fall_through; |
||||
|
// Branch to fall through will not negate, because it is a
|
||||
|
// forward-only target.
|
||||
|
fall_through.Branch(NegateCondition(cc), NegateHint(hint)); |
||||
|
Jump(arg); // May emit merge code here.
|
||||
|
fall_through.Bind(); |
||||
|
} else { |
||||
|
#ifdef DEBUG |
||||
|
Result::Type arg_type = arg->type(); |
||||
|
Register arg_reg = arg->is_register() ? arg->reg() : no_reg; |
||||
|
#endif |
||||
|
cgen()->frame()->Push(arg); |
||||
|
DoBranch(cc, hint); |
||||
|
*arg = cgen()->frame()->Pop(); |
||||
|
ASSERT(arg->type() == arg_type); |
||||
|
ASSERT(!arg->is_register() || arg->reg().is(arg_reg)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Bind(Result* arg) { |
||||
|
if (cgen()->has_valid_frame()) { |
||||
|
cgen()->frame()->Push(arg); |
||||
|
} |
||||
|
DoBind(); |
||||
|
*arg = cgen()->frame()->Pop(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Bind(Result* arg0, Result* arg1) { |
||||
|
if (cgen()->has_valid_frame()) { |
||||
|
cgen()->frame()->Push(arg0); |
||||
|
cgen()->frame()->Push(arg1); |
||||
|
} |
||||
|
DoBind(); |
||||
|
*arg1 = cgen()->frame()->Pop(); |
||||
|
*arg0 = cgen()->frame()->Pop(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::ComputeEntryFrame() { |
||||
|
// Given: a collection of frames reaching by forward CFG edges and
|
||||
|
// the directionality of the block. Compute: an entry frame for the
|
||||
|
// block.
|
||||
|
|
||||
|
Counters::compute_entry_frame.Increment(); |
||||
|
#ifdef DEBUG |
||||
|
if (compiling_deferred_code_) { |
||||
|
ASSERT(reaching_frames_.length() > 1); |
||||
|
VirtualFrame* frame = reaching_frames_[0]; |
||||
|
bool all_identical = true; |
||||
|
for (int i = 1; i < reaching_frames_.length(); i++) { |
||||
|
if (!frame->Equals(reaching_frames_[i])) { |
||||
|
all_identical = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
ASSERT(!all_identical || all_identical); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
// Choose an initial frame.
|
||||
|
VirtualFrame* initial_frame = reaching_frames_[0]; |
||||
|
|
||||
|
// A list of pointers to frame elements in the entry frame. NULL
|
||||
|
// indicates that the element has not yet been determined.
|
||||
|
int length = initial_frame->element_count(); |
||||
|
ZoneList<FrameElement*> elements(length); |
||||
|
|
||||
|
// Initially populate the list of elements based on the initial
|
||||
|
// frame.
|
||||
|
for (int i = 0; i < length; i++) { |
||||
|
FrameElement element = initial_frame->elements_[i]; |
||||
|
// We do not allow copies or constants in bidirectional frames.
|
||||
|
if (direction_ == BIDIRECTIONAL) { |
||||
|
if (element.is_constant() || element.is_copy()) { |
||||
|
elements.Add(NULL); |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
elements.Add(&initial_frame->elements_[i]); |
||||
|
} |
||||
|
|
||||
|
// Compute elements based on the other reaching frames.
|
||||
|
if (reaching_frames_.length() > 1) { |
||||
|
for (int i = 0; i < length; i++) { |
||||
|
FrameElement* element = elements[i]; |
||||
|
for (int j = 1; j < reaching_frames_.length(); j++) { |
||||
|
// Element computation is monotonic: new information will not
|
||||
|
// change our decision about undetermined or invalid elements.
|
||||
|
if (element == NULL || !element->is_valid()) break; |
||||
|
|
||||
|
FrameElement* other = &reaching_frames_[j]->elements_[i]; |
||||
|
element = element->Combine(other); |
||||
|
if (element != NULL && !element->is_copy()) { |
||||
|
ASSERT(other != NULL); |
||||
|
// We overwrite the number information of one of the incoming frames.
|
||||
|
// This is safe because we only use the frame for emitting merge code.
|
||||
|
// The number information of incoming frames is not used anymore.
|
||||
|
element->set_type_info(TypeInfo::Combine(element->type_info(), |
||||
|
other->type_info())); |
||||
|
} |
||||
|
} |
||||
|
elements[i] = element; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Build the new frame. A freshly allocated frame has memory elements
|
||||
|
// for the parameters and some platform-dependent elements (e.g.,
|
||||
|
// return address). Replace those first.
|
||||
|
entry_frame_ = new VirtualFrame(); |
||||
|
int index = 0; |
||||
|
for (; index < entry_frame_->element_count(); index++) { |
||||
|
FrameElement* target = elements[index]; |
||||
|
// If the element is determined, set it now. Count registers. Mark
|
||||
|
// elements as copied exactly when they have a copy. Undetermined
|
||||
|
// elements are initially recorded as if in memory.
|
||||
|
if (target != NULL) { |
||||
|
entry_frame_->elements_[index] = *target; |
||||
|
InitializeEntryElement(index, target); |
||||
|
} |
||||
|
} |
||||
|
// Then fill in the rest of the frame with new elements.
|
||||
|
for (; index < length; index++) { |
||||
|
FrameElement* target = elements[index]; |
||||
|
if (target == NULL) { |
||||
|
entry_frame_->elements_.Add( |
||||
|
FrameElement::MemoryElement(TypeInfo::Uninitialized())); |
||||
|
} else { |
||||
|
entry_frame_->elements_.Add(*target); |
||||
|
InitializeEntryElement(index, target); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Allocate any still-undetermined frame elements to registers or
|
||||
|
// memory, from the top down.
|
||||
|
for (int i = length - 1; i >= 0; i--) { |
||||
|
if (elements[i] == NULL) { |
||||
|
// Loop over all the reaching frames to check whether the element
|
||||
|
// is synced on all frames and to count the registers it occupies.
|
||||
|
bool is_synced = true; |
||||
|
RegisterFile candidate_registers; |
||||
|
int best_count = kMinInt; |
||||
|
int best_reg_num = RegisterAllocator::kInvalidRegister; |
||||
|
TypeInfo info = TypeInfo::Uninitialized(); |
||||
|
|
||||
|
for (int j = 0; j < reaching_frames_.length(); j++) { |
||||
|
FrameElement element = reaching_frames_[j]->elements_[i]; |
||||
|
if (direction_ == BIDIRECTIONAL) { |
||||
|
info = TypeInfo::Unknown(); |
||||
|
} else if (!element.is_copy()) { |
||||
|
info = TypeInfo::Combine(info, element.type_info()); |
||||
|
} else { |
||||
|
// New elements will not be copies, so get number information from
|
||||
|
// backing element in the reaching frame.
|
||||
|
info = TypeInfo::Combine(info, |
||||
|
reaching_frames_[j]->elements_[element.index()].type_info()); |
||||
|
} |
||||
|
is_synced = is_synced && element.is_synced(); |
||||
|
if (element.is_register() && !entry_frame_->is_used(element.reg())) { |
||||
|
// Count the register occurrence and remember it if better
|
||||
|
// than the previous best.
|
||||
|
int num = RegisterAllocator::ToNumber(element.reg()); |
||||
|
candidate_registers.Use(num); |
||||
|
if (candidate_registers.count(num) > best_count) { |
||||
|
best_count = candidate_registers.count(num); |
||||
|
best_reg_num = num; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// We must have a number type information now (not for copied elements).
|
||||
|
ASSERT(entry_frame_->elements_[i].is_copy() |
||||
|
|| !info.IsUninitialized()); |
||||
|
|
||||
|
// If the value is synced on all frames, put it in memory. This
|
||||
|
// costs nothing at the merge code but will incur a
|
||||
|
// memory-to-register move when the value is needed later.
|
||||
|
if (is_synced) { |
||||
|
// Already recorded as a memory element.
|
||||
|
// Set combined number info.
|
||||
|
entry_frame_->elements_[i].set_type_info(info); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// Try to put it in a register. If there was no best choice
|
||||
|
// consider any free register.
|
||||
|
if (best_reg_num == RegisterAllocator::kInvalidRegister) { |
||||
|
for (int j = 0; j < RegisterAllocator::kNumRegisters; j++) { |
||||
|
if (!entry_frame_->is_used(j)) { |
||||
|
best_reg_num = j; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (best_reg_num != RegisterAllocator::kInvalidRegister) { |
||||
|
// If there was a register choice, use it. Preserve the copied
|
||||
|
// flag on the element.
|
||||
|
bool is_copied = entry_frame_->elements_[i].is_copied(); |
||||
|
Register reg = RegisterAllocator::ToRegister(best_reg_num); |
||||
|
entry_frame_->elements_[i] = |
||||
|
FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, |
||||
|
TypeInfo::Uninitialized()); |
||||
|
if (is_copied) entry_frame_->elements_[i].set_copied(); |
||||
|
entry_frame_->set_register_location(reg, i); |
||||
|
} |
||||
|
// Set combined number info.
|
||||
|
entry_frame_->elements_[i].set_type_info(info); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// If we have incoming backward edges assert we forget all number information.
|
||||
|
#ifdef DEBUG |
||||
|
if (direction_ == BIDIRECTIONAL) { |
||||
|
for (int i = 0; i < length; ++i) { |
||||
|
if (!entry_frame_->elements_[i].is_copy()) { |
||||
|
ASSERT(entry_frame_->elements_[i].type_info().IsUnknown()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
// The stack pointer is at the highest synced element or the base of
|
||||
|
// the expression stack.
|
||||
|
int stack_pointer = length - 1; |
||||
|
while (stack_pointer >= entry_frame_->expression_base_index() && |
||||
|
!entry_frame_->elements_[stack_pointer].is_synced()) { |
||||
|
stack_pointer--; |
||||
|
} |
||||
|
entry_frame_->stack_pointer_ = stack_pointer; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
DeferredCode::DeferredCode() |
||||
|
: masm_(CodeGeneratorScope::Current()->masm()), |
||||
|
statement_position_(masm_->current_statement_position()), |
||||
|
position_(masm_->current_position()) { |
||||
|
ASSERT(statement_position_ != RelocInfo::kNoPosition); |
||||
|
ASSERT(position_ != RelocInfo::kNoPosition); |
||||
|
|
||||
|
CodeGeneratorScope::Current()->AddDeferred(this); |
||||
|
#ifdef DEBUG |
||||
|
comment_ = ""; |
||||
|
#endif |
||||
|
|
||||
|
// Copy the register locations from the code generator's frame.
|
||||
|
// These are the registers that will be spilled on entry to the
|
||||
|
// deferred code and restored on exit.
|
||||
|
VirtualFrame* frame = CodeGeneratorScope::Current()->frame(); |
||||
|
int sp_offset = frame->fp_relative(frame->stack_pointer_); |
||||
|
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { |
||||
|
int loc = frame->register_location(i); |
||||
|
if (loc == VirtualFrame::kIllegalIndex) { |
||||
|
registers_[i] = kIgnore; |
||||
|
} else if (frame->elements_[loc].is_synced()) { |
||||
|
// Needs to be restored on exit but not saved on entry.
|
||||
|
registers_[i] = frame->fp_relative(loc) | kSyncedFlag; |
||||
|
} else { |
||||
|
int offset = frame->fp_relative(loc); |
||||
|
registers_[i] = (offset < sp_offset) ? kPush : offset; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,42 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#ifndef V8_JUMP_TARGET_LIGHT_INL_H_ |
||||
|
#define V8_JUMP_TARGET_LIGHT_INL_H_ |
||||
|
|
||||
|
#include "virtual-frame-inl.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
void JumpTarget::InitializeEntryElement(int index, FrameElement* target) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_JUMP_TARGET_LIGHT_INL_H_
|
@ -0,0 +1,99 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#include "v8.h" |
||||
|
|
||||
|
#include "codegen-inl.h" |
||||
|
#include "jump-target-inl.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Jump(Result* arg) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Bind(Result* arg) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::Bind(Result* arg0, Result* arg1) { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JumpTarget::ComputeEntryFrame() { |
||||
|
UNIMPLEMENTED(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
DeferredCode::DeferredCode() |
||||
|
: masm_(CodeGeneratorScope::Current()->masm()), |
||||
|
statement_position_(masm_->current_statement_position()), |
||||
|
position_(masm_->current_position()) { |
||||
|
ASSERT(statement_position_ != RelocInfo::kNoPosition); |
||||
|
ASSERT(position_ != RelocInfo::kNoPosition); |
||||
|
|
||||
|
CodeGeneratorScope::Current()->AddDeferred(this); |
||||
|
#ifdef DEBUG |
||||
|
comment_ = ""; |
||||
|
#endif |
||||
|
|
||||
|
// Copy the register locations from the code generator's frame.
|
||||
|
// These are the registers that will be spilled on entry to the
|
||||
|
// deferred code and restored on exit.
|
||||
|
VirtualFrame* frame = CodeGeneratorScope::Current()->frame(); |
||||
|
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { |
||||
|
int loc = frame->register_location(i); |
||||
|
if (loc == VirtualFrame::kIllegalIndex) { |
||||
|
registers_[i] = kIgnore; |
||||
|
} else { |
||||
|
// Needs to be restored on exit but not saved on entry.
|
||||
|
registers_[i] = frame->fp_relative(loc) | kSyncedFlag; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,431 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
// LiveEdit feature implementation. The script should be executed after
|
||||
|
// debug-debugger.js.
|
||||
|
|
||||
|
|
||||
|
// Changes script text and recompiles all relevant functions if possible.
|
||||
|
// The change is always a substring (change_pos, change_pos + change_len)
|
||||
|
// being replaced with a completely different string new_str.
|
||||
|
//
|
||||
|
// Only one function will have its Code changed in result of this function.
|
||||
|
// All nested functions (should they have any instances at the moment) are left
|
||||
|
// unchanged and re-linked to a newly created script instance representing old
|
||||
|
// version of the source. (Generally speaking,
|
||||
|
// during the change all nested functions are erased and completely different
|
||||
|
// set of nested functions are introduced.) All other functions just have
|
||||
|
// their positions updated.
|
||||
|
//
|
||||
|
// @param {Script} script that is being changed
|
||||
|
// @param {Array} change_log a list that collects engineer-readable description
|
||||
|
// of what happened.
|
||||
|
Debug.LiveEditChangeScript = function(script, change_pos, change_len, new_str, |
||||
|
change_log) { |
||||
|
|
||||
|
// So far the function works as namespace.
|
||||
|
var liveedit = Debug.LiveEditChangeScript; |
||||
|
var Assert = liveedit.Assert; |
||||
|
|
||||
|
// Fully compiles source string as a script. Returns Array of
|
||||
|
// FunctionCompileInfo -- a descriptions of all functions of the script.
|
||||
|
// Elements of array are ordered by start positions of functions (from top
|
||||
|
// to bottom) in the source. Fields outer_index and next_sibling_index help
|
||||
|
// to navigate the nesting structure of functions.
|
||||
|
//
|
||||
|
// The script is used for compilation, because it produces code that
|
||||
|
// needs to be linked with some particular script (for nested functions).
|
||||
|
function DebugGatherCompileInfo(source) { |
||||
|
// Get function info, elements are partially sorted (it is a tree
|
||||
|
// of nested functions serialized as parent followed by serialized children.
|
||||
|
var raw_compile_info = %LiveEditGatherCompileInfo(script, source); |
||||
|
|
||||
|
// Sort function infos by start position field.
|
||||
|
var compile_info = new Array(); |
||||
|
var old_index_map = new Array(); |
||||
|
for (var i = 0; i < raw_compile_info.length; i++) { |
||||
|
compile_info.push(new liveedit.FunctionCompileInfo(raw_compile_info[i])); |
||||
|
old_index_map.push(i); |
||||
|
} |
||||
|
|
||||
|
for (var i = 0; i < compile_info.length; i++) { |
||||
|
var k = i; |
||||
|
for (var j = i + 1; j < compile_info.length; j++) { |
||||
|
if (compile_info[k].start_position > compile_info[j].start_position) { |
||||
|
k = j; |
||||
|
} |
||||
|
} |
||||
|
if (k != i) { |
||||
|
var temp_info = compile_info[k]; |
||||
|
var temp_index = old_index_map[k]; |
||||
|
compile_info[k] = compile_info[i]; |
||||
|
old_index_map[k] = old_index_map[i]; |
||||
|
compile_info[i] = temp_info; |
||||
|
old_index_map[i] = temp_index; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// After sorting update outer_inder field using old_index_map. Also
|
||||
|
// set next_sibling_index field.
|
||||
|
var current_index = 0; |
||||
|
|
||||
|
// The recursive function, that goes over all children of a particular
|
||||
|
// node (i.e. function info).
|
||||
|
function ResetIndexes(new_parent_index, old_parent_index) { |
||||
|
var previous_sibling = -1; |
||||
|
while (current_index < compile_info.length && |
||||
|
compile_info[current_index].outer_index == old_parent_index) { |
||||
|
var saved_index = current_index; |
||||
|
compile_info[saved_index].outer_index = new_parent_index; |
||||
|
if (previous_sibling != -1) { |
||||
|
compile_info[previous_sibling].next_sibling_index = saved_index; |
||||
|
} |
||||
|
previous_sibling = saved_index; |
||||
|
current_index++; |
||||
|
ResetIndexes(saved_index, old_index_map[saved_index]); |
||||
|
} |
||||
|
if (previous_sibling != -1) { |
||||
|
compile_info[previous_sibling].next_sibling_index = -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ResetIndexes(-1, -1); |
||||
|
Assert(current_index == compile_info.length); |
||||
|
|
||||
|
return compile_info; |
||||
|
} |
||||
|
|
||||
|
// Given a positions, finds a function that fully includes the entire change.
|
||||
|
function FindChangedFunction(compile_info, offset, len) { |
||||
|
// First condition: function should start before the change region.
|
||||
|
// Function #0 (whole-script function) always does, but we want
|
||||
|
// one, that is later in this list.
|
||||
|
var index = 0; |
||||
|
while (index + 1 < compile_info.length && |
||||
|
compile_info[index + 1].start_position <= offset) { |
||||
|
index++; |
||||
|
} |
||||
|
// Now we are at the last function that begins before the change
|
||||
|
// region. The function that covers entire change region is either
|
||||
|
// this function or the enclosing one.
|
||||
|
for (; compile_info[index].end_position < offset + len; |
||||
|
index = compile_info[index].outer_index) { |
||||
|
Assert(index != -1); |
||||
|
} |
||||
|
return index; |
||||
|
} |
||||
|
|
||||
|
// Variable forward declarations. Preprocessor "Minifier" needs them.
|
||||
|
var old_compile_info; |
||||
|
var shared_infos; |
||||
|
// Finds SharedFunctionInfo that corresponds compile info with index
|
||||
|
// in old version of the script.
|
||||
|
function FindFunctionInfo(index) { |
||||
|
var old_info = old_compile_info[index]; |
||||
|
for (var i = 0; i < shared_infos.length; i++) { |
||||
|
var info = shared_infos[i]; |
||||
|
if (info.start_position == old_info.start_position && |
||||
|
info.end_position == old_info.end_position) { |
||||
|
return info; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Replaces function's Code.
|
||||
|
function PatchCode(new_info, shared_info) { |
||||
|
%LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array); |
||||
|
|
||||
|
change_log.push( {function_patched: new_info.function_name} ); |
||||
|
} |
||||
|
|
||||
|
var change_len_old; |
||||
|
var change_len_new; |
||||
|
// Translate position in old version of script into position in new
|
||||
|
// version of script.
|
||||
|
function PosTranslator(old_pos) { |
||||
|
if (old_pos <= change_pos) { |
||||
|
return old_pos; |
||||
|
} |
||||
|
if (old_pos >= change_pos + change_len_old) { |
||||
|
return old_pos + change_len_new - change_len_old; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
var position_change_array; |
||||
|
var position_patch_report; |
||||
|
function PatchPositions(new_info, shared_info) { |
||||
|
if (!shared_info) { |
||||
|
// TODO: explain what is happening.
|
||||
|
return; |
||||
|
} |
||||
|
%LiveEditPatchFunctionPositions(shared_info.raw_array, |
||||
|
position_change_array); |
||||
|
position_patch_report.push( { name: new_info.function_name } ); |
||||
|
} |
||||
|
|
||||
|
var link_to_old_script_report; |
||||
|
var old_script; |
||||
|
// Makes a function associated with another instance of a script (the
|
||||
|
// one representing its old version). This way the function still
|
||||
|
// may access its own text.
|
||||
|
function LinkToOldScript(shared_info) { |
||||
|
%LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script); |
||||
|
|
||||
|
link_to_old_script_report.push( { name: shared_info.function_name } ); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
var old_source = script.source; |
||||
|
var change_len_old = change_len; |
||||
|
var change_len_new = new_str.length; |
||||
|
|
||||
|
// Prepare new source string.
|
||||
|
var new_source = old_source.substring(0, change_pos) + |
||||
|
new_str + old_source.substring(change_pos + change_len); |
||||
|
|
||||
|
// Find all SharedFunctionInfo's that are compiled from this script.
|
||||
|
var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script); |
||||
|
|
||||
|
var shared_infos = new Array(); |
||||
|
|
||||
|
for (var i = 0; i < shared_raw_list.length; i++) { |
||||
|
shared_infos.push(new liveedit.SharedInfoWrapper(shared_raw_list[i])); |
||||
|
} |
||||
|
|
||||
|
// Gather compile information about old version of script.
|
||||
|
var old_compile_info = DebugGatherCompileInfo(old_source); |
||||
|
|
||||
|
// Gather compile information about new version of script.
|
||||
|
var new_compile_info; |
||||
|
try { |
||||
|
new_compile_info = DebugGatherCompileInfo(new_source); |
||||
|
} catch (e) { |
||||
|
throw new liveedit.Failure("Failed to compile new version of script: " + e); |
||||
|
} |
||||
|
|
||||
|
// An index of a single function, that is going to have its code replaced.
|
||||
|
var function_being_patched = |
||||
|
FindChangedFunction(old_compile_info, change_pos, change_len_old); |
||||
|
|
||||
|
// In old and new script versions function with a change should have the
|
||||
|
// same indexes.
|
||||
|
var function_being_patched2 = |
||||
|
FindChangedFunction(new_compile_info, change_pos, change_len_new); |
||||
|
Assert(function_being_patched == function_being_patched2, |
||||
|
"inconsistent old/new compile info"); |
||||
|
|
||||
|
// Check that function being patched has the same expectations in a new
|
||||
|
// version. Otherwise we cannot safely patch its behavior and should
|
||||
|
// choose the outer function instead.
|
||||
|
while (!liveedit.CompareFunctionExpectations( |
||||
|
old_compile_info[function_being_patched], |
||||
|
new_compile_info[function_being_patched])) { |
||||
|
|
||||
|
Assert(old_compile_info[function_being_patched].outer_index == |
||||
|
new_compile_info[function_being_patched].outer_index); |
||||
|
function_being_patched = |
||||
|
old_compile_info[function_being_patched].outer_index; |
||||
|
Assert(function_being_patched != -1); |
||||
|
} |
||||
|
|
||||
|
// Check that function being patched is not currently on stack.
|
||||
|
liveedit.CheckStackActivations( |
||||
|
[ FindFunctionInfo(function_being_patched) ], change_log ); |
||||
|
|
||||
|
|
||||
|
// Committing all changes.
|
||||
|
var old_script_name = liveedit.CreateNameForOldScript(script); |
||||
|
|
||||
|
// Update the script text and create a new script representing an old
|
||||
|
// version of the script.
|
||||
|
var old_script = %LiveEditReplaceScript(script, new_source, old_script_name); |
||||
|
|
||||
|
PatchCode(new_compile_info[function_being_patched], |
||||
|
FindFunctionInfo(function_being_patched)); |
||||
|
|
||||
|
var position_patch_report = new Array(); |
||||
|
change_log.push( {position_patched: position_patch_report} ); |
||||
|
|
||||
|
var position_change_array = [ change_pos, |
||||
|
change_pos + change_len_old, |
||||
|
change_pos + change_len_new ]; |
||||
|
|
||||
|
// Update positions of all outer functions (i.e. all functions, that
|
||||
|
// are partially below the function being patched).
|
||||
|
for (var i = new_compile_info[function_being_patched].outer_index; |
||||
|
i != -1; |
||||
|
i = new_compile_info[i].outer_index) { |
||||
|
PatchPositions(new_compile_info[i], FindFunctionInfo(i)); |
||||
|
} |
||||
|
|
||||
|
// Update positions of all functions that are fully below the function
|
||||
|
// being patched.
|
||||
|
var old_next_sibling = |
||||
|
old_compile_info[function_being_patched].next_sibling_index; |
||||
|
var new_next_sibling = |
||||
|
new_compile_info[function_being_patched].next_sibling_index; |
||||
|
|
||||
|
// We simply go over the tail of both old and new lists. Their tails should
|
||||
|
// have an identical structure.
|
||||
|
if (old_next_sibling == -1) { |
||||
|
Assert(new_next_sibling == -1); |
||||
|
} else { |
||||
|
Assert(old_compile_info.length - old_next_sibling == |
||||
|
new_compile_info.length - new_next_sibling); |
||||
|
|
||||
|
for (var i = old_next_sibling, j = new_next_sibling; |
||||
|
i < old_compile_info.length; i++, j++) { |
||||
|
PatchPositions(new_compile_info[j], FindFunctionInfo(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var link_to_old_script_report = new Array(); |
||||
|
change_log.push( { linked_to_old_script: link_to_old_script_report } ); |
||||
|
|
||||
|
// We need to link to old script all former nested functions.
|
||||
|
for (var i = function_being_patched + 1; i < old_next_sibling; i++) { |
||||
|
LinkToOldScript(FindFunctionInfo(i), old_script); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Debug.LiveEditChangeScript.Assert = function(condition, message) { |
||||
|
if (!condition) { |
||||
|
if (message) { |
||||
|
throw "Assert " + message; |
||||
|
} else { |
||||
|
throw "Assert"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// An object describing function compilation details. Its index fields
|
||||
|
// apply to indexes inside array that stores these objects.
|
||||
|
Debug.LiveEditChangeScript.FunctionCompileInfo = function(raw_array) { |
||||
|
this.function_name = raw_array[0]; |
||||
|
this.start_position = raw_array[1]; |
||||
|
this.end_position = raw_array[2]; |
||||
|
this.param_num = raw_array[3]; |
||||
|
this.code = raw_array[4]; |
||||
|
this.scope_info = raw_array[5]; |
||||
|
this.outer_index = raw_array[6]; |
||||
|
this.next_sibling_index = null; |
||||
|
this.raw_array = raw_array; |
||||
|
} |
||||
|
|
||||
|
// A structure describing SharedFunctionInfo.
|
||||
|
Debug.LiveEditChangeScript.SharedInfoWrapper = function(raw_array) { |
||||
|
this.function_name = raw_array[0]; |
||||
|
this.start_position = raw_array[1]; |
||||
|
this.end_position = raw_array[2]; |
||||
|
this.info = raw_array[3]; |
||||
|
this.raw_array = raw_array; |
||||
|
} |
||||
|
|
||||
|
// Adds a suffix to script name to mark that it is old version.
|
||||
|
Debug.LiveEditChangeScript.CreateNameForOldScript = function(script) { |
||||
|
// TODO(635): try better than this; support several changes.
|
||||
|
return script.name + " (old)"; |
||||
|
} |
||||
|
|
||||
|
// Compares a function interface old and new version, whether it
|
||||
|
// changed or not.
|
||||
|
Debug.LiveEditChangeScript.CompareFunctionExpectations = |
||||
|
function(function_info1, function_info2) { |
||||
|
// Check that function has the same number of parameters (there may exist
|
||||
|
// an adapter, that won't survive function parameter number change).
|
||||
|
if (function_info1.param_num != function_info2.param_num) { |
||||
|
return false; |
||||
|
} |
||||
|
var scope_info1 = function_info1.scope_info; |
||||
|
var scope_info2 = function_info2.scope_info; |
||||
|
|
||||
|
if (!scope_info1) { |
||||
|
return !scope_info2; |
||||
|
} |
||||
|
|
||||
|
if (scope_info1.length != scope_info2.length) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Check that outer scope structure is not changed. Otherwise the function
|
||||
|
// will not properly work with existing scopes.
|
||||
|
return scope_info1.toString() == scope_info2.toString(); |
||||
|
} |
||||
|
|
||||
|
// For array of wrapped shared function infos checks that none of them
|
||||
|
// have activations on stack (of any thread). Throws a Failure exception
|
||||
|
// if this proves to be false.
|
||||
|
Debug.LiveEditChangeScript.CheckStackActivations = function(shared_wrapper_list, |
||||
|
change_log) { |
||||
|
var liveedit = Debug.LiveEditChangeScript; |
||||
|
|
||||
|
var shared_list = new Array(); |
||||
|
for (var i = 0; i < shared_wrapper_list.length; i++) { |
||||
|
shared_list[i] = shared_wrapper_list[i].info; |
||||
|
} |
||||
|
var result = %LiveEditCheckStackActivations(shared_list); |
||||
|
var problems = new Array(); |
||||
|
for (var i = 0; i < shared_list.length; i++) { |
||||
|
if (result[i] == liveedit.FunctionPatchabilityStatus.FUNCTION_BLOCKED_ON_STACK) { |
||||
|
var shared = shared_list[i]; |
||||
|
var description = { |
||||
|
name: shared.function_name, |
||||
|
start_pos: shared.start_position, |
||||
|
end_pos: shared.end_position |
||||
|
}; |
||||
|
problems.push(description); |
||||
|
} |
||||
|
} |
||||
|
if (problems.length > 0) { |
||||
|
change_log.push( { functions_on_stack: problems } ); |
||||
|
throw new liveedit.Failure("Blocked by functions on stack"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// A copy of the FunctionPatchabilityStatus enum from liveedit.h
|
||||
|
Debug.LiveEditChangeScript.FunctionPatchabilityStatus = { |
||||
|
FUNCTION_AVAILABLE_FOR_PATCH: 0, |
||||
|
FUNCTION_BLOCKED_ON_STACK: 1 |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// A logical failure in liveedit process. This means that change_log
|
||||
|
// is valid and consistent description of what happened.
|
||||
|
Debug.LiveEditChangeScript.Failure = function(message) { |
||||
|
this.message = message; |
||||
|
} |
||||
|
|
||||
|
Debug.LiveEditChangeScript.Failure.prototype.toString = function() { |
||||
|
return "LiveEdit Failure: " + this.message; |
||||
|
} |
||||
|
|
||||
|
// A testing entry.
|
||||
|
Debug.LiveEditChangeScript.GetPcFromSourcePos = function(func, source_pos) { |
||||
|
return %GetFunctionCodePositionFromSource(func, source_pos); |
||||
|
} |
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue