Stack trace API: poison stack frames below the first strict mode frame.
Function and receiver objects are not accessible for poisoned frames. R=rossberg@chromium.org BUG=v8:2564 Review URL: https://chromiumcodereview.appspot.com/13150003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14085 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
15a39131d2
commit
9155d20282
@ -612,13 +612,16 @@ Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
|
||||
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()) {
|
||||
@ -646,6 +649,17 @@ Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
|
||||
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);
|
||||
@ -653,6 +667,7 @@ Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
|
||||
}
|
||||
}
|
||||
}
|
||||
elements->set(0, Smi::FromInt(non_strict_frames));
|
||||
Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
|
||||
result->set_length(Smi::FromInt(cursor));
|
||||
return result;
|
||||
|
102
src/messages.js
102
src/messages.js
@ -746,64 +746,70 @@ function GetPositionInLine(message) {
|
||||
|
||||
|
||||
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";
|
||||
}
|
||||
@ -813,26 +819,22 @@ function CallSiteGetFunctionName() {
|
||||
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;
|
||||
@ -847,49 +849,49 @@ function CallSiteGetMethodName() {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -932,7 +934,7 @@ 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 &&
|
||||
@ -1036,13 +1038,15 @@ function FormatErrorString(error) {
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1070,16 +1074,16 @@ function FormatStackTrace(error_string, 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;
|
||||
}
|
||||
|
122
test/mjsunit/regress/regress-2564.js
Normal file
122
test/mjsunit/regress/regress-2564.js
Normal file
@ -0,0 +1,122 @@
|
||||
// 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);
|
||||
};
|
Loading…
Reference in New Issue
Block a user