mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
8 changed files with 0 additions and 6975 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,268 +0,0 @@ |
|||||
// 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(); |
|
@ -1,431 +0,0 @@ |
|||||
// 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-delay.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
@ -1,190 +0,0 @@ |
|||||
// 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_NUMBER_INFO_H_ |
|
||||
#define V8_NUMBER_INFO_H_ |
|
||||
|
|
||||
namespace v8 { |
|
||||
namespace internal { |
|
||||
|
|
||||
// Unknown
|
|
||||
// |
|
|
||||
// Number
|
|
||||
// / |
|
|
||||
// HeapNumber Integer32
|
|
||||
// | |
|
|
||||
// | Smi
|
|
||||
// | /
|
|
||||
// Uninitialized.
|
|
||||
|
|
||||
class NumberInfo { |
|
||||
public: |
|
||||
NumberInfo() { } |
|
||||
|
|
||||
static inline NumberInfo Unknown(); |
|
||||
// We know it's a number of some sort.
|
|
||||
static inline NumberInfo Number(); |
|
||||
// We know it's signed or unsigned 32 bit integer.
|
|
||||
static inline NumberInfo Integer32(); |
|
||||
// We know it's a Smi.
|
|
||||
static inline NumberInfo Smi(); |
|
||||
// We know it's a heap number.
|
|
||||
static inline NumberInfo HeapNumber(); |
|
||||
// We haven't started collecting info yet.
|
|
||||
static inline NumberInfo Uninitialized(); |
|
||||
|
|
||||
// Return compact representation. Very sensitive to enum values below!
|
|
||||
int ThreeBitRepresentation() { |
|
||||
ASSERT(type_ != kUninitializedType); |
|
||||
int answer = type_ > 6 ? type_ -2 : type_; |
|
||||
ASSERT(answer >= 0); |
|
||||
ASSERT(answer <= 7); |
|
||||
return answer; |
|
||||
} |
|
||||
|
|
||||
// Decode compact representation. Very sensitive to enum values below!
|
|
||||
static NumberInfo ExpandedRepresentation(int three_bit_representation) { |
|
||||
Type t = static_cast<Type>(three_bit_representation >= 6 ? |
|
||||
three_bit_representation + 2 : |
|
||||
three_bit_representation); |
|
||||
ASSERT(t == kUnknownType || |
|
||||
t == kNumberType || |
|
||||
t == kInteger32Type || |
|
||||
t == kSmiType || |
|
||||
t == kHeapNumberType); |
|
||||
return NumberInfo(t); |
|
||||
} |
|
||||
|
|
||||
int ToInt() { |
|
||||
return type_; |
|
||||
} |
|
||||
|
|
||||
static NumberInfo FromInt(int bit_representation) { |
|
||||
Type t = static_cast<Type>(bit_representation); |
|
||||
ASSERT(t == kUnknownType || |
|
||||
t == kNumberType || |
|
||||
t == kInteger32Type || |
|
||||
t == kSmiType || |
|
||||
t == kHeapNumberType); |
|
||||
return NumberInfo(t); |
|
||||
} |
|
||||
|
|
||||
// Return the weakest (least precise) common type.
|
|
||||
static NumberInfo Combine(NumberInfo a, NumberInfo b) { |
|
||||
return NumberInfo(static_cast<Type>(a.type_ & b.type_)); |
|
||||
} |
|
||||
|
|
||||
inline bool IsUnknown() { |
|
||||
return type_ == kUnknownType; |
|
||||
} |
|
||||
|
|
||||
inline bool IsNumber() { |
|
||||
ASSERT(type_ != kUninitializedType); |
|
||||
return ((type_ & kNumberType) == kNumberType); |
|
||||
} |
|
||||
|
|
||||
inline bool IsSmi() { |
|
||||
ASSERT(type_ != kUninitializedType); |
|
||||
return ((type_ & kSmiType) == kSmiType); |
|
||||
} |
|
||||
|
|
||||
inline bool IsInteger32() { |
|
||||
ASSERT(type_ != kUninitializedType); |
|
||||
return ((type_ & kInteger32Type) == kInteger32Type); |
|
||||
} |
|
||||
|
|
||||
inline bool IsHeapNumber() { |
|
||||
ASSERT(type_ != kUninitializedType); |
|
||||
return ((type_ & kHeapNumberType) == kHeapNumberType); |
|
||||
} |
|
||||
|
|
||||
inline bool IsUninitialized() { |
|
||||
return type_ == kUninitializedType; |
|
||||
} |
|
||||
|
|
||||
const char* ToString() { |
|
||||
switch (type_) { |
|
||||
case kUnknownType: return "UnknownType"; |
|
||||
case kNumberType: return "NumberType"; |
|
||||
case kSmiType: return "SmiType"; |
|
||||
case kHeapNumberType: return "HeapNumberType"; |
|
||||
case kInteger32Type: return "Integer32Type"; |
|
||||
case kUninitializedType: |
|
||||
UNREACHABLE(); |
|
||||
return "UninitializedType"; |
|
||||
} |
|
||||
UNREACHABLE(); |
|
||||
return "Unreachable code"; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
enum Type { |
|
||||
kUnknownType = 0, |
|
||||
kNumberType = 1, |
|
||||
kInteger32Type = 3, |
|
||||
kSmiType = 7, |
|
||||
kHeapNumberType = 9, |
|
||||
kUninitializedType = 15 |
|
||||
}; |
|
||||
explicit inline NumberInfo(Type t) : type_(t) { } |
|
||||
|
|
||||
Type type_; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::Unknown() { |
|
||||
return NumberInfo(kUnknownType); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::Number() { |
|
||||
return NumberInfo(kNumberType); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::Integer32() { |
|
||||
return NumberInfo(kInteger32Type); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::Smi() { |
|
||||
return NumberInfo(kSmiType); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::HeapNumber() { |
|
||||
return NumberInfo(kHeapNumberType); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NumberInfo NumberInfo::Uninitialized() { |
|
||||
return NumberInfo(kUninitializedType); |
|
||||
} |
|
||||
|
|
||||
} } // namespace v8::internal
|
|
||||
|
|
||||
#endif // V8_NUMBER_INFO_H_
|
|
@ -1,497 +0,0 @@ |
|||||
// Copyright 2006-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.
|
|
||||
|
|
||||
// Expect $Object = global.Object;
|
|
||||
// Expect $Array = global.Array;
|
|
||||
|
|
||||
const $RegExp = global.RegExp; |
|
||||
|
|
||||
// A recursive descent parser for Patterns according to the grammar of
|
|
||||
// ECMA-262 15.10.1, with deviations noted below.
|
|
||||
function DoConstructRegExp(object, pattern, flags, isConstructorCall) { |
|
||||
// RegExp : Called as constructor; see ECMA-262, section 15.10.4.
|
|
||||
if (IS_REGEXP(pattern)) { |
|
||||
if (!IS_UNDEFINED(flags)) { |
|
||||
throw MakeTypeError('regexp_flags', []); |
|
||||
} |
|
||||
flags = (pattern.global ? 'g' : '') |
|
||||
+ (pattern.ignoreCase ? 'i' : '') |
|
||||
+ (pattern.multiline ? 'm' : ''); |
|
||||
pattern = pattern.source; |
|
||||
} |
|
||||
|
|
||||
pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern); |
|
||||
flags = IS_UNDEFINED(flags) ? '' : ToString(flags); |
|
||||
|
|
||||
var global = false; |
|
||||
var ignoreCase = false; |
|
||||
var multiline = false; |
|
||||
|
|
||||
for (var i = 0; i < flags.length; i++) { |
|
||||
var c = StringCharAt.call(flags, i); |
|
||||
switch (c) { |
|
||||
case 'g': |
|
||||
// Allow duplicate flags to be consistent with JSC and others.
|
|
||||
global = true; |
|
||||
break; |
|
||||
case 'i': |
|
||||
ignoreCase = true; |
|
||||
break; |
|
||||
case 'm': |
|
||||
multiline = true; |
|
||||
break; |
|
||||
default: |
|
||||
// Ignore flags that have no meaning to be consistent with
|
|
||||
// JSC.
|
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (isConstructorCall) { |
|
||||
// ECMA-262, section 15.10.7.1.
|
|
||||
%SetProperty(object, 'source', pattern, |
|
||||
DONT_DELETE | READ_ONLY | DONT_ENUM); |
|
||||
|
|
||||
// ECMA-262, section 15.10.7.2.
|
|
||||
%SetProperty(object, 'global', global, DONT_DELETE | READ_ONLY | DONT_ENUM); |
|
||||
|
|
||||
// ECMA-262, section 15.10.7.3.
|
|
||||
%SetProperty(object, 'ignoreCase', ignoreCase, |
|
||||
DONT_DELETE | READ_ONLY | DONT_ENUM); |
|
||||
|
|
||||
// ECMA-262, section 15.10.7.4.
|
|
||||
%SetProperty(object, 'multiline', multiline, |
|
||||
DONT_DELETE | READ_ONLY | DONT_ENUM); |
|
||||
|
|
||||
// ECMA-262, section 15.10.7.5.
|
|
||||
%SetProperty(object, 'lastIndex', 0, DONT_DELETE | DONT_ENUM); |
|
||||
} else { // RegExp is being recompiled via RegExp.prototype.compile.
|
|
||||
%IgnoreAttributesAndSetProperty(object, 'source', pattern); |
|
||||
%IgnoreAttributesAndSetProperty(object, 'global', global); |
|
||||
%IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase); |
|
||||
%IgnoreAttributesAndSetProperty(object, 'multiline', multiline); |
|
||||
%IgnoreAttributesAndSetProperty(object, 'lastIndex', 0); |
|
||||
regExpCache.type = 'none'; |
|
||||
} |
|
||||
|
|
||||
// Call internal function to compile the pattern.
|
|
||||
%RegExpCompile(object, pattern, flags); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpConstructor(pattern, flags) { |
|
||||
if (%_IsConstructCall()) { |
|
||||
DoConstructRegExp(this, pattern, flags, true); |
|
||||
} else { |
|
||||
// RegExp : Called as function; see ECMA-262, section 15.10.3.1.
|
|
||||
if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) { |
|
||||
return pattern; |
|
||||
} |
|
||||
return new $RegExp(pattern, flags); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// Deprecated RegExp.prototype.compile method. We behave like the constructor
|
|
||||
// were called again. In SpiderMonkey, this method returns the regexp object.
|
|
||||
// In JSC, it returns undefined. For compatibility with JSC, we match their
|
|
||||
// behavior.
|
|
||||
function CompileRegExp(pattern, flags) { |
|
||||
// Both JSC and SpiderMonkey treat a missing pattern argument as the
|
|
||||
// empty subject string, and an actual undefined value passed as the
|
|
||||
// pattern as the string 'undefined'. Note that JSC is inconsistent
|
|
||||
// here, treating undefined values differently in
|
|
||||
// RegExp.prototype.compile and in the constructor, where they are
|
|
||||
// the empty string. For compatibility with JSC, we match their
|
|
||||
// behavior.
|
|
||||
if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { |
|
||||
DoConstructRegExp(this, 'undefined', flags, false); |
|
||||
} else { |
|
||||
DoConstructRegExp(this, pattern, flags, false); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function DoRegExpExec(regexp, string, index) { |
|
||||
return %_RegExpExec(regexp, string, index, lastMatchInfo); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpCache() { |
|
||||
this.type = 'none'; |
|
||||
this.regExp = 0; |
|
||||
this.subject = 0; |
|
||||
this.replaceString = 0; |
|
||||
this.lastIndex = 0; |
|
||||
this.answer = 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
var regExpCache = new RegExpCache(); |
|
||||
|
|
||||
|
|
||||
function CloneRegexpAnswer(array) { |
|
||||
var len = array.length; |
|
||||
var answer = new $Array(len); |
|
||||
for (var i = 0; i < len; i++) { |
|
||||
answer[i] = array[i]; |
|
||||
} |
|
||||
answer.index = array.index; |
|
||||
answer.input = array.input; |
|
||||
return answer; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpExec(string) { |
|
||||
if (!IS_REGEXP(this)) { |
|
||||
throw MakeTypeError('incompatible_method_receiver', |
|
||||
['RegExp.prototype.exec', this]); |
|
||||
} |
|
||||
|
|
||||
var cache = regExpCache; |
|
||||
|
|
||||
if (%_ObjectEquals(cache.type, 'exec') && |
|
||||
%_ObjectEquals(cache.lastIndex, this.lastIndex) && |
|
||||
%_ObjectEquals(cache.regExp, this) && |
|
||||
%_ObjectEquals(cache.subject, string)) { |
|
||||
var last = cache.answer; |
|
||||
if (last == null) { |
|
||||
return last; |
|
||||
} else { |
|
||||
return CloneRegexpAnswer(last); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (%_ArgumentsLength() == 0) { |
|
||||
var regExpInput = LAST_INPUT(lastMatchInfo); |
|
||||
if (IS_UNDEFINED(regExpInput)) { |
|
||||
throw MakeError('no_input_to_regexp', [this]); |
|
||||
} |
|
||||
string = regExpInput; |
|
||||
} |
|
||||
var s; |
|
||||
if (IS_STRING(string)) { |
|
||||
s = string; |
|
||||
} else { |
|
||||
s = ToString(string); |
|
||||
} |
|
||||
var lastIndex = this.lastIndex; |
|
||||
|
|
||||
var i = this.global ? TO_INTEGER(lastIndex) : 0; |
|
||||
|
|
||||
if (i < 0 || i > s.length) { |
|
||||
this.lastIndex = 0; |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); |
|
||||
// matchIndices is either null or the lastMatchInfo array.
|
|
||||
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); |
|
||||
|
|
||||
if (matchIndices == null) { |
|
||||
if (this.global) this.lastIndex = 0; |
|
||||
cache.lastIndex = lastIndex; |
|
||||
cache.regExp = this; |
|
||||
cache.subject = s; |
|
||||
cache.answer = matchIndices; // Null.
|
|
||||
cache.type = 'exec'; |
|
||||
return matchIndices; // No match.
|
|
||||
} |
|
||||
|
|
||||
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; |
|
||||
var result; |
|
||||
if (numResults === 1) { |
|
||||
var matchStart = lastMatchInfo[CAPTURE(0)]; |
|
||||
var matchEnd = lastMatchInfo[CAPTURE(1)]; |
|
||||
result = [SubString(s, matchStart, matchEnd)]; |
|
||||
} else { |
|
||||
result = new $Array(numResults); |
|
||||
for (var i = 0; i < numResults; i++) { |
|
||||
var matchStart = lastMatchInfo[CAPTURE(i << 1)]; |
|
||||
var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)]; |
|
||||
if (matchStart != -1 && matchEnd != -1) { |
|
||||
result[i] = SubString(s, matchStart, matchEnd); |
|
||||
} else { |
|
||||
// Make sure the element is present. Avoid reading the undefined
|
|
||||
// property from the global object since this may change.
|
|
||||
result[i] = void 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
result.index = lastMatchInfo[CAPTURE0]; |
|
||||
result.input = s; |
|
||||
if (this.global) { |
|
||||
this.lastIndex = lastMatchInfo[CAPTURE1]; |
|
||||
return result; |
|
||||
} else { |
|
||||
cache.regExp = this; |
|
||||
cache.subject = s; |
|
||||
cache.lastIndex = lastIndex; |
|
||||
cache.answer = result; |
|
||||
cache.type = 'exec'; |
|
||||
return CloneRegexpAnswer(result); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
|
|
||||
// that test is defined in terms of String.prototype.exec. However, it probably
|
|
||||
// means the original value of String.prototype.exec, which is what everybody
|
|
||||
// else implements.
|
|
||||
function RegExpTest(string) { |
|
||||
if (!IS_REGEXP(this)) { |
|
||||
throw MakeTypeError('incompatible_method_receiver', |
|
||||
['RegExp.prototype.test', this]); |
|
||||
} |
|
||||
if (%_ArgumentsLength() == 0) { |
|
||||
var regExpInput = LAST_INPUT(lastMatchInfo); |
|
||||
if (IS_UNDEFINED(regExpInput)) { |
|
||||
throw MakeError('no_input_to_regexp', [this]); |
|
||||
} |
|
||||
string = regExpInput; |
|
||||
} |
|
||||
var s; |
|
||||
if (IS_STRING(string)) { |
|
||||
s = string; |
|
||||
} else { |
|
||||
s = ToString(string); |
|
||||
} |
|
||||
|
|
||||
var lastIndex = this.lastIndex; |
|
||||
|
|
||||
var cache = regExpCache; |
|
||||
|
|
||||
if (%_ObjectEquals(cache.type, 'test') && |
|
||||
%_ObjectEquals(cache.regExp, this) && |
|
||||
%_ObjectEquals(cache.subject, string) && |
|
||||
%_ObjectEquals(cache.lastIndex, lastIndex)) { |
|
||||
return cache.answer; |
|
||||
} |
|
||||
|
|
||||
var length = s.length; |
|
||||
var i = this.global ? TO_INTEGER(lastIndex) : 0; |
|
||||
|
|
||||
cache.type = 'test'; |
|
||||
cache.regExp = this; |
|
||||
cache.subject = s; |
|
||||
cache.lastIndex = i; |
|
||||
|
|
||||
if (i < 0 || i > s.length) { |
|
||||
this.lastIndex = 0; |
|
||||
cache.answer = false; |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); |
|
||||
// matchIndices is either null or the lastMatchInfo array.
|
|
||||
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); |
|
||||
|
|
||||
if (matchIndices == null) { |
|
||||
if (this.global) this.lastIndex = 0; |
|
||||
cache.answer = false; |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; |
|
||||
cache.answer = true; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpToString() { |
|
||||
// If this.source is an empty string, output /(?:)/.
|
|
||||
// http://bugzilla.mozilla.org/show_bug.cgi?id=225550
|
|
||||
// ecma_2/RegExp/properties-001.js.
|
|
||||
var src = this.source ? this.source : '(?:)'; |
|
||||
var result = '/' + src + '/'; |
|
||||
if (this.global) |
|
||||
result += 'g'; |
|
||||
if (this.ignoreCase) |
|
||||
result += 'i'; |
|
||||
if (this.multiline) |
|
||||
result += 'm'; |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// Getters for the static properties lastMatch, lastParen, leftContext, and
|
|
||||
// rightContext of the RegExp constructor. The properties are computed based
|
|
||||
// on the captures array of the last successful match and the subject string
|
|
||||
// of the last successful match.
|
|
||||
function RegExpGetLastMatch() { |
|
||||
var regExpSubject = LAST_SUBJECT(lastMatchInfo); |
|
||||
return SubString(regExpSubject, |
|
||||
lastMatchInfo[CAPTURE0], |
|
||||
lastMatchInfo[CAPTURE1]); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpGetLastParen() { |
|
||||
var length = NUMBER_OF_CAPTURES(lastMatchInfo); |
|
||||
if (length <= 2) return ''; // There were no captures.
|
|
||||
// We match the SpiderMonkey behavior: return the substring defined by the
|
|
||||
// last pair (after the first pair) of elements of the capture array even if
|
|
||||
// it is empty.
|
|
||||
var regExpSubject = LAST_SUBJECT(lastMatchInfo); |
|
||||
var start = lastMatchInfo[CAPTURE(length - 2)]; |
|
||||
var end = lastMatchInfo[CAPTURE(length - 1)]; |
|
||||
if (start != -1 && end != -1) { |
|
||||
return SubString(regExpSubject, start, end); |
|
||||
} |
|
||||
return ""; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpGetLeftContext() { |
|
||||
return SubString(LAST_SUBJECT(lastMatchInfo), |
|
||||
0, |
|
||||
lastMatchInfo[CAPTURE0]); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
function RegExpGetRightContext() { |
|
||||
var subject = LAST_SUBJECT(lastMatchInfo); |
|
||||
return SubString(subject, |
|
||||
lastMatchInfo[CAPTURE1], |
|
||||
subject.length); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// The properties $1..$9 are the first nine capturing substrings of the last
|
|
||||
// successful match, or ''. The function RegExpMakeCaptureGetter will be
|
|
||||
// called with indices from 1 to 9.
|
|
||||
function RegExpMakeCaptureGetter(n) { |
|
||||
return function() { |
|
||||
var index = n * 2; |
|
||||
if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return ''; |
|
||||
var matchStart = lastMatchInfo[CAPTURE(index)]; |
|
||||
var matchEnd = lastMatchInfo[CAPTURE(index + 1)]; |
|
||||
if (matchStart == -1 || matchEnd == -1) return ''; |
|
||||
return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// Property of the builtins object for recording the result of the last
|
|
||||
// regexp match. The property lastMatchInfo includes the matchIndices
|
|
||||
// array of the last successful regexp match (an array of start/end index
|
|
||||
// pairs for the match and all the captured substrings), the invariant is
|
|
||||
// that there are at least two capture indeces. The array also contains
|
|
||||
// the subject string for the last successful match.
|
|
||||
var lastMatchInfo = [ |
|
||||
2, // REGEXP_NUMBER_OF_CAPTURES
|
|
||||
"", // Last subject.
|
|
||||
void 0, // Last input - settable with RegExpSetInput.
|
|
||||
0, // REGEXP_FIRST_CAPTURE + 0
|
|
||||
0, // REGEXP_FIRST_CAPTURE + 1
|
|
||||
]; |
|
||||
|
|
||||
// -------------------------------------------------------------------
|
|
||||
|
|
||||
function SetupRegExp() { |
|
||||
%FunctionSetInstanceClassName($RegExp, 'RegExp'); |
|
||||
%FunctionSetPrototype($RegExp, new $Object()); |
|
||||
%SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM); |
|
||||
%SetCode($RegExp, RegExpConstructor); |
|
||||
|
|
||||
InstallFunctions($RegExp.prototype, DONT_ENUM, $Array( |
|
||||
"exec", RegExpExec, |
|
||||
"test", RegExpTest, |
|
||||
"toString", RegExpToString, |
|
||||
"compile", CompileRegExp |
|
||||
)); |
|
||||
|
|
||||
// The length of compile is 1 in SpiderMonkey.
|
|
||||
%FunctionSetLength($RegExp.prototype.compile, 1); |
|
||||
|
|
||||
// The properties input, $input, and $_ are aliases for each other. When this
|
|
||||
// value is set the value it is set to is coerced to a string.
|
|
||||
// Getter and setter for the input.
|
|
||||
function RegExpGetInput() { |
|
||||
var regExpInput = LAST_INPUT(lastMatchInfo); |
|
||||
return IS_UNDEFINED(regExpInput) ? "" : regExpInput; |
|
||||
} |
|
||||
function RegExpSetInput(string) { |
|
||||
regExpCache.type = 'none'; |
|
||||
LAST_INPUT(lastMatchInfo) = ToString(string); |
|
||||
}; |
|
||||
|
|
||||
%DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); |
|
||||
|
|
||||
// The properties multiline and $* are aliases for each other. When this
|
|
||||
// value is set in SpiderMonkey, the value it is set to is coerced to a
|
|
||||
// boolean. We mimic that behavior with a slight difference: in SpiderMonkey
|
|
||||
// the value of the expression 'RegExp.multiline = null' (for instance) is the
|
|
||||
// boolean false (ie, the value after coercion), while in V8 it is the value
|
|
||||
// null (ie, the value before coercion).
|
|
||||
|
|
||||
// Getter and setter for multiline.
|
|
||||
var multiline = false; |
|
||||
function RegExpGetMultiline() { return multiline; }; |
|
||||
function RegExpSetMultiline(flag) { multiline = flag ? true : false; }; |
|
||||
|
|
||||
%DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE); |
|
||||
|
|
||||
|
|
||||
function NoOpSetter(ignored) {} |
|
||||
|
|
||||
|
|
||||
// Static properties set by a successful match.
|
|
||||
%DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); |
|
||||
|
|
||||
for (var i = 1; i < 10; ++i) { |
|
||||
%DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE); |
|
||||
%DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
SetupRegExp(); |
|
Loading…
Reference in new issue