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