Commit 9155d202 authored by yangguo@chromium.org's avatar yangguo@chromium.org

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
parent 15a39131
......@@ -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;
......
......@@ -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;
}
......
// 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);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment