limit = Max(limit, 0); // Ensure that limit is not negative.
int initial_size = Min(limit, 10);
Handle<FixedArray> elements =
- factory()->NewFixedArrayWithHoles(initial_size * 4);
+ factory()->NewFixedArrayWithHoles(initial_size * 4 + 1);
// If the caller parameter is a function we skip frames until we're
// under it before starting to collect.
bool seen_caller = !caller->IsJSFunction();
- int cursor = 0;
+ // First element is reserved to store the number of non-strict frames.
+ int cursor = 1;
int frames_seen = 0;
+ int non_strict_frames = 0;
+ bool encountered_strict_function = false;
for (StackFrameIterator iter(this);
!iter.done() && frames_seen < limit;
iter.Advance()) {
Handle<JSFunction> fun = frames[i].function();
Handle<Code> code = frames[i].code();
Handle<Smi> offset(Smi::FromInt(frames[i].offset()), this);
+ // The stack trace API should not expose receivers and function
+ // objects on frames deeper than the top-most one with a strict
+ // mode function. The number of non-strict frames is stored as
+ // first element in the result array.
+ if (!encountered_strict_function) {
+ if (!fun->shared()->is_classic_mode()) {
+ encountered_strict_function = true;
+ } else {
+ non_strict_frames++;
+ }
+ }
elements->set(cursor++, *recv);
elements->set(cursor++, *fun);
elements->set(cursor++, *code);
}
}
}
+ elements->set(0, Smi::FromInt(non_strict_frames));
Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
result->set_length(Smi::FromInt(cursor));
return result;
function GetStackTraceLine(recv, fun, pos, isGlobal) {
- return new CallSite(recv, fun, pos).toString();
+ return new CallSite(recv, fun, pos, false).toString();
}
// ----------------------------------------------------------------------------
// Error implementation
-function CallSite(receiver, fun, pos) {
- this.receiver = receiver;
- this.fun = fun;
- this.pos = pos;
+var CallSiteReceiverKey = %CreateSymbol("receiver");
+var CallSiteFunctionKey = %CreateSymbol("function");
+var CallSitePositionKey = %CreateSymbol("position");
+var CallSiteStrictModeKey = %CreateSymbol("strict mode");
+
+function CallSite(receiver, fun, pos, strict_mode) {
+ this[CallSiteReceiverKey] = receiver;
+ this[CallSiteFunctionKey] = fun;
+ this[CallSitePositionKey] = pos;
+ this[CallSiteStrictModeKey] = strict_mode;
}
function CallSiteGetThis() {
- return this.receiver;
+ return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteReceiverKey];
}
function CallSiteGetTypeName() {
- return GetTypeName(this, false);
+ return GetTypeName(this[CallSiteReceiverKey], false);
}
function CallSiteIsToplevel() {
- if (this.receiver == null) {
+ if (this[CallSiteReceiverKey] == null) {
return true;
}
- return IS_GLOBAL(this.receiver);
+ return IS_GLOBAL(this[CallSiteReceiverKey]);
}
function CallSiteIsEval() {
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
return script && script.compilation_type == COMPILATION_TYPE_EVAL;
}
function CallSiteGetEvalOrigin() {
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
return FormatEvalOrigin(script);
}
function CallSiteGetScriptNameOrSourceURL() {
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
return script ? script.nameOrSourceURL() : null;
}
function CallSiteGetFunction() {
- return this.fun;
+ return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteFunctionKey];
}
function CallSiteGetFunctionName() {
// See if the function knows its own name
- var name = this.fun.name;
+ var name = this[CallSiteFunctionKey].name;
if (name) {
return name;
}
- name = %FunctionGetInferredName(this.fun);
+ name = %FunctionGetInferredName(this[CallSiteFunctionKey]);
if (name) {
return name;
}
// Maybe this is an evaluation?
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
return "eval";
}
function CallSiteGetMethodName() {
// See if we can find a unique property on the receiver that holds
// this function.
- var ownName = this.fun.name;
- if (ownName && this.receiver &&
- (%_CallFunction(this.receiver,
- ownName,
- ObjectLookupGetter) === this.fun ||
- %_CallFunction(this.receiver,
- ownName,
- ObjectLookupSetter) === this.fun ||
- (IS_OBJECT(this.receiver) &&
- %GetDataProperty(this.receiver, ownName) === this.fun))) {
+ var receiver = this[CallSiteReceiverKey];
+ var fun = this[CallSiteFunctionKey];
+ var ownName = fun.name;
+ if (ownName && receiver &&
+ (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
+ %_CallFunction(receiver, ownName, ObjectLookupSetter) === fun ||
+ (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) {
// To handle DontEnum properties we guess that the method has
// the same name as the function.
return ownName;
}
var name = null;
- for (var prop in this.receiver) {
- if (%_CallFunction(this.receiver, prop, ObjectLookupGetter) === this.fun ||
- %_CallFunction(this.receiver, prop, ObjectLookupSetter) === this.fun ||
- (IS_OBJECT(this.receiver) &&
- %GetDataProperty(this.receiver, prop) === this.fun)) {
+ for (var prop in receiver) {
+ if (%_CallFunction(receiver, prop, ObjectLookupGetter) === fun ||
+ %_CallFunction(receiver, prop, ObjectLookupSetter) === fun ||
+ (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) {
// If we find more than one match bail out to avoid confusion.
if (name) {
return null;
}
function CallSiteGetFileName() {
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
return script ? script.name : null;
}
function CallSiteGetLineNumber() {
- if (this.pos == -1) {
+ if (this[CallSitePositionKey] == -1) {
return null;
}
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
var location = null;
if (script) {
- location = script.locationFromPosition(this.pos, true);
+ location = script.locationFromPosition(this[CallSitePositionKey], true);
}
return location ? location.line + 1 : null;
}
function CallSiteGetColumnNumber() {
- if (this.pos == -1) {
+ if (this[CallSitePositionKey] == -1) {
return null;
}
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
var location = null;
if (script) {
- location = script.locationFromPosition(this.pos, true);
+ location = script.locationFromPosition(this[CallSitePositionKey], true);
}
return location ? location.column + 1: null;
}
function CallSiteIsNative() {
- var script = %FunctionGetScript(this.fun);
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
return script ? (script.type == TYPE_NATIVE) : false;
}
function CallSiteGetPosition() {
- return this.pos;
+ return this[CallSitePositionKey];
}
function CallSiteIsConstructor() {
- var receiver = this.receiver;
+ var receiver = this[CallSiteReceiverKey];
var constructor =
IS_OBJECT(receiver) ? %GetDataProperty(receiver, "constructor") : null;
if (!constructor) return false;
- return this.fun === constructor;
+ return this[CallSiteFunctionKey] === constructor;
}
function CallSiteToString() {
var isConstructor = this.isConstructor();
var isMethodCall = !(this.isToplevel() || isConstructor);
if (isMethodCall) {
- var typeName = GetTypeName(this, true);
+ var typeName = GetTypeName(this[CallSiteReceiverKey], true);
var methodName = this.getMethodName();
if (functionName) {
if (typeName &&
function GetStackFrames(raw_stack) {
var frames = new InternalArray();
- for (var i = 0; i < raw_stack.length; i += 4) {
+ var non_strict_frames = raw_stack[0];
+ for (var i = 1; 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));
+ non_strict_frames--;
+ frames.push(new CallSite(recv, fun, pos, (non_strict_frames < 0)));
}
return frames;
}
}
-function GetTypeName(obj, requireConstructor) {
- var constructor = obj.receiver.constructor;
+function GetTypeName(receiver, requireConstructor) {
+ var constructor = receiver.constructor;
if (!constructor) {
return requireConstructor ? null :
- %_CallFunction(obj.receiver, ObjectToString);
+ %_CallFunction(receiver, ObjectToString);
}
var constructorName = constructor.name;
if (!constructorName) {
return requireConstructor ? null :
- %_CallFunction(obj.receiver, ObjectToString);
+ %_CallFunction(receiver, ObjectToString);
}
return constructorName;
}
--- /dev/null
+// Copyright 2013 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 o = [ function f0() { throw new Error(); },
+ function f1() { o[0](); },
+ function f2() { o[1](); },
+ function f3() { o[2](); } ];
+
+Error.prepareStackTrace = function(error, frames) {
+ Error.prepareStackTrace = undefined; // Prevent recursion.
+ try {
+ assertEquals(5, frames.length);
+ // Don't check the last frame since that's the top-level code.
+ for (var i = 0; i < frames.length - 1; i++) {
+ assertEquals(o[i], frames[i].getFunction());
+ assertEquals(o, frames[i].getThis());
+ // Private fields are no longer accessible.
+ assertEquals(undefined, frames[i].receiver);
+ assertEquals(undefined, frames[i].fun);
+ assertEquals(undefined, frames[i].pos);
+ }
+ return "success";
+ } catch (e) {
+ return "fail";
+ }
+}
+
+try {
+ o[3]();
+} catch (e) {
+ assertEquals("success", e.stack);
+};
+
+
+var o = [ function f0() { throw new Error(); },
+ function f1() { o[0](); },
+ function f2() { "use strict"; o[1](); },
+ function f3() { o[2](); } ];
+
+Error.prepareStackTrace = function(error, frames) {
+ Error.prepareStackTrace = undefined; // Prevent recursion.
+ try {
+ assertEquals(5, frames.length);
+ for (var i = 0; i < 2; i++) {
+ // The first two frames are still classic mode.
+ assertEquals(o[i], frames[i].getFunction());
+ assertEquals(o, frames[i].getThis());
+ }
+ for (var i = 2; i < frames.length; i++) {
+ // The rest are poisoned by the first strict mode function.
+ assertEquals(undefined, frames[i].getFunction());
+ assertEquals(undefined, frames[i].getThis());
+ }
+ for (var i = 0; i < frames.length - 1; i++) {
+ // Function name remains accessible.
+ assertEquals("f"+i, frames[i].getFunctionName());
+ }
+ return "success";
+ } catch (e) {
+ return e;
+ }
+}
+
+try {
+ o[3]();
+} catch (e) {
+ assertEquals("success", e.stack);
+};
+
+
+var o = [ function f0() { "use strict"; throw new Error(); },
+ function f1() { o[0](); },
+ function f2() { o[1](); },
+ function f3() { o[2](); } ];
+
+Error.prepareStackTrace = function(error, frames) {
+ Error.prepareStackTrace = undefined; // Prevent recursion.
+ try {
+ assertEquals(5, frames.length);
+ for (var i = 0; i < frames.length; i++) {
+ // The rest are poisoned by the first strict mode function.
+ assertEquals(undefined, frames[i].getFunction());
+ assertEquals(undefined, frames[i].getThis());
+ if (i < frames.length - 1) { // Function name remains accessible.
+ assertEquals("f"+i, frames[i].getFunctionName());
+ }
+ }
+ return "success";
+ } catch (e) {
+ return e;
+ }
+}
+
+try {
+ o[3]();
+} catch (e) {
+ assertEquals("success", e.stack);
+};