Index: src/messages.js |
diff --git a/src/messages.js b/src/messages.js |
index 5d065d098434f39743d7d35a20524e22d973f22a..4a8143e611162a447436ecb64aefb670e10c14fb 100644 |
--- a/src/messages.js |
+++ b/src/messages.js |
@@ -820,7 +820,7 @@ function CallSiteGetMethodName() { |
%_CallFunction(this.receiver, |
ownName, |
ObjectLookupSetter) === this.fun || |
- this.receiver[ownName] === this.fun)) { |
+ %GetDataProperty(this.receiver, ownName) === this.fun)) { |
// To handle DontEnum properties we guess that the method has |
// the same name as the function. |
return ownName; |
@@ -829,8 +829,7 @@ function CallSiteGetMethodName() { |
for (var prop in this.receiver) { |
if (%_CallFunction(this.receiver, prop, ObjectLookupGetter) === this.fun || |
%_CallFunction(this.receiver, prop, ObjectLookupSetter) === this.fun || |
- (!%_CallFunction(this.receiver, prop, ObjectLookupGetter) && |
- this.receiver[prop] === this.fun)) { |
+ %GetDataProperty(this.receiver, prop) === this.fun) { |
// If we find more than one match bail out to avoid confusion. |
if (name) { |
return null; |
@@ -883,7 +882,8 @@ function CallSiteGetPosition() { |
} |
function CallSiteIsConstructor() { |
- var constructor = this.receiver ? this.receiver.constructor : null; |
+ var receiver = this.receiver; |
+ var constructor = receiver ? %GetDataProperty(receiver, "constructor") : null; |
if (!constructor) { |
return false; |
} |
@@ -933,12 +933,14 @@ function CallSiteToString() { |
var typeName = GetTypeName(this, true); |
var methodName = this.getMethodName(); |
if (functionName) { |
- if (typeName && functionName.indexOf(typeName) != 0) { |
+ if (typeName && |
+ %_CallFunction(functionName, typeName, StringIndexOf) != 0) { |
line += typeName + "."; |
} |
line += functionName; |
- if (methodName && functionName.lastIndexOf("." + methodName) != |
- functionName.length - methodName.length - 1) { |
+ if (methodName && |
+ (%_CallFunction(functionName, "." + methodName, StringIndexOf) != |
+ functionName.length - methodName.length - 1)) { |
line += " [as " + methodName + "]"; |
} |
} else { |
@@ -1016,17 +1018,37 @@ function FormatEvalOrigin(script) { |
return eval_origin; |
} |
-function FormatStackTrace(error, frames) { |
- var lines = []; |
+ |
+function FormatErrorString(error) { |
try { |
- lines.push(error.toString()); |
+ return %_CallFunction(error, ErrorToString); |
} catch (e) { |
try { |
- lines.push("<error: " + e + ">"); |
+ return "<error: " + e + ">"; |
} catch (ee) { |
- lines.push("<error>"); |
+ return "<error>"; |
} |
} |
+} |
+ |
+ |
+function GetStackFrames(raw_stack) { |
+ var frames = new InternalArray(); |
+ for (var i = 0; i < raw_stack.length; i += 4) { |
+ var recv = raw_stack[i]; |
+ var fun = raw_stack[i + 1]; |
+ var code = raw_stack[i + 2]; |
+ var pc = raw_stack[i + 3]; |
+ var pos = %FunctionGetPositionForOffset(code, pc); |
+ frames.push(new CallSite(recv, fun, pos)); |
+ } |
+ return frames; |
+} |
+ |
+ |
+function FormatStackTrace(error_string, frames) { |
+ var lines = new InternalArray(); |
+ lines.push(error_string); |
for (var i = 0; i < frames.length; i++) { |
var frame = frames[i]; |
var line; |
@@ -1042,25 +1064,9 @@ function FormatStackTrace(error, frames) { |
} |
lines.push(" at " + line); |
} |
- return lines.join("\n"); |
+ return %_CallFunction(lines, "\n", ArrayJoin); |
} |
-function FormatRawStackTrace(error, raw_stack) { |
- var frames = [ ]; |
- for (var i = 0; i < raw_stack.length; i += 4) { |
- var recv = raw_stack[i]; |
- var fun = raw_stack[i + 1]; |
- var code = raw_stack[i + 2]; |
- var pc = raw_stack[i + 3]; |
- var pos = %FunctionGetPositionForOffset(code, pc); |
- frames.push(new CallSite(recv, fun, pos)); |
- } |
- if (IS_FUNCTION($Error.prepareStackTrace)) { |
- return $Error.prepareStackTrace(error, frames); |
- } else { |
- return FormatStackTrace(error, frames); |
- } |
-} |
function GetTypeName(obj, requireConstructor) { |
var constructor = obj.receiver.constructor; |
@@ -1076,23 +1082,51 @@ function GetTypeName(obj, requireConstructor) { |
return constructorName; |
} |
+ |
+// Flag to prevent recursive call of Error.prepareStackTrace. |
+var formatting_custom_stack_trace = false; |
+ |
+ |
function captureStackTrace(obj, cons_opt) { |
var stackTraceLimit = $Error.stackTraceLimit; |
if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return; |
if (stackTraceLimit < 0 || stackTraceLimit > 10000) { |
stackTraceLimit = 10000; |
} |
- var raw_stack = %CollectStackTrace(obj, |
- cons_opt ? cons_opt : captureStackTrace, |
- stackTraceLimit); |
+ var stack = %CollectStackTrace(obj, |
+ cons_opt ? cons_opt : captureStackTrace, |
+ stackTraceLimit); |
+ |
+ // Don't be lazy if the error stack formatting is custom (observable). |
+ if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) { |
+ var array = []; |
+ %MoveArrayContents(GetStackFrames(stack), array); |
+ formatting_custom_stack_trace = true; |
+ try { |
+ obj.stack = $Error.prepareStackTrace(obj, array); |
+ } catch (e) { |
+ throw e; // The custom formatting function threw. Rethrow. |
+ } finally { |
+ formatting_custom_stack_trace = false; |
+ } |
+ return; |
+ } |
+ |
+ var error_string = FormatErrorString(obj); |
// Note that 'obj' and 'this' maybe different when called on objects that |
// have the error object on its prototype chain. The getter replaces itself |
// with a data property as soon as the stack trace has been formatted. |
+ // The getter must not change the object layout as it may be called after GC. |
var getter = function() { |
- var value = FormatRawStackTrace(obj, raw_stack); |
- %DefineOrRedefineDataProperty(obj, 'stack', value, NONE); |
- return value; |
+ if (IS_STRING(stack)) return stack; |
+ // Stack is still a raw array awaiting to be formatted. |
+ stack = FormatStackTrace(error_string, GetStackFrames(stack)); |
+ // Release context value. |
+ error_string = void 0; |
+ return stack; |
}; |
+ %MarkOneShotGetter(getter); |
+ |
// The 'stack' property of the receiver is set as data property. If |
// the receiver is the same as holder, this accessor pair is replaced. |
var setter = function(v) { |
@@ -1239,23 +1273,32 @@ function SetUpStackOverflowBoilerplate() { |
// error object copy, but can be found on the prototype chain of 'this'. |
// When the stack trace is formatted, this accessor property is replaced by |
// a data property. |
+ var error_string = boilerplate.name + ": " + boilerplate.message; |
+ |
+ // The getter must not change the object layout as it may be called after GC. |
function getter() { |
var holder = this; |
while (!IS_ERROR(holder)) { |
holder = %GetPrototype(holder); |
if (holder == null) return MakeSyntaxError('illegal_access', []); |
} |
- var raw_stack = %GetOverflowedRawStackTrace(holder); |
- var result = IS_ARRAY(raw_stack) ? FormatRawStackTrace(holder, raw_stack) |
- : void 0; |
- %DefineOrRedefineDataProperty(holder, 'stack', result, NONE); |
- return result; |
+ var stack = %GetOverflowedStackTrace(holder); |
+ if (IS_STRING(stack)) return stack; |
+ if (IS_ARRAY(stack)) { |
+ var result = FormatStackTrace(error_string, GetStackFrames(stack)); |
+ %SetOverflowedStackTrace(holder, result); |
+ return result; |
+ } |
+ return void 0; |
} |
+ %MarkOneShotGetter(getter); |
// The 'stack' property of the receiver is set as data property. If |
// the receiver is the same as holder, this accessor pair is replaced. |
function setter(v) { |
%DefineOrRedefineDataProperty(this, 'stack', v, NONE); |
+ // Release the stack trace that is stored as hidden property, if exists. |
+ %SetOverflowedStackTrace(this, void 0); |
} |
%DefineOrRedefineAccessorProperty( |