зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset ace4dd426349 (bug 972045) for mochitest failures.
This commit is contained in:
Родитель
7840ab12de
Коммит
8b7b5cc3a2
|
@ -512,8 +512,7 @@ struct CompartmentStats
|
|||
macro(Other, NotLiveGCThing, compartmentObject) \
|
||||
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
|
||||
macro(Other, NotLiveGCThing, regexpCompartment) \
|
||||
macro(Other, NotLiveGCThing, debuggeesSet) \
|
||||
macro(Other, IsLiveGCThing, savedStacksSet)
|
||||
macro(Other, NotLiveGCThing, debuggeesSet)
|
||||
|
||||
CompartmentStats()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
@ -851,25 +850,6 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetSavedFrameCount(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(cx->compartment()->savedStacks().count());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SaveStack(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Rooted<SavedFrame*> frame(cx);
|
||||
if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
|
||||
return false;
|
||||
args.rval().setObject(*frame.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
static bool
|
||||
OOMAfterAllocations(JSContext *cx, unsigned argc, jsval *vp)
|
||||
|
@ -1570,15 +1550,6 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
" then you can provide an extra argument with some specific traceable\n"
|
||||
" thing to count.\n"),
|
||||
|
||||
JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
|
||||
"getSavedFrameCount()",
|
||||
" Return the number of SavedFrame instances stored in this compartment's\n"
|
||||
" SavedStacks cache."),
|
||||
|
||||
JS_FN_HELP("saveStack", SaveStack, 0, 0,
|
||||
"saveStack()",
|
||||
" Capture a stack.\n"),
|
||||
|
||||
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
|
||||
"oomAfterAllocations(count)",
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// The SavedFrame constructor shouldn't have been exposed to JS on the global.
|
||||
|
||||
assertEq(typeof SavedFrame, "undefined");
|
|
@ -1,38 +0,0 @@
|
|||
// Test that we can save stacks with direct and indirect eval calls.
|
||||
|
||||
const directEval = (function iife() {
|
||||
return eval("(" + function evalFrame() {
|
||||
return saveStack();
|
||||
} + "())");
|
||||
}());
|
||||
|
||||
assertEq(directEval.source.contains("> eval"), true);
|
||||
assertEq(directEval.functionDisplayName, "evalFrame");
|
||||
|
||||
assertEq(directEval.parent.source.contains("> eval"), true);
|
||||
|
||||
assertEq(directEval.parent.parent.source.contains("> eval"), false);
|
||||
assertEq(directEval.parent.parent.functionDisplayName, "iife");
|
||||
|
||||
assertEq(directEval.parent.parent.parent.source.contains("> eval"), false);
|
||||
|
||||
assertEq(directEval.parent.parent.parent.parent, null);
|
||||
|
||||
const lave = eval;
|
||||
const indirectEval = (function iife() {
|
||||
return lave("(" + function evalFrame() {
|
||||
return saveStack();
|
||||
} + "())");
|
||||
}());
|
||||
|
||||
assertEq(indirectEval.source.contains("> eval"), true);
|
||||
assertEq(indirectEval.functionDisplayName, "evalFrame");
|
||||
|
||||
assertEq(indirectEval.parent.source.contains("> eval"), true);
|
||||
|
||||
assertEq(indirectEval.parent.parent.source.contains("> eval"), false);
|
||||
assertEq(indirectEval.parent.parent.functionDisplayName, "iife");
|
||||
|
||||
assertEq(indirectEval.parent.parent.parent.source.contains("> eval"), false);
|
||||
|
||||
assertEq(indirectEval.parent.parent.parent.parent, null);
|
|
@ -1,17 +0,0 @@
|
|||
// Test the functionDisplayName of SavedFrame instances.
|
||||
|
||||
function uno() { return dos(); }
|
||||
const dos = () => tres.quattro();
|
||||
const tres = {
|
||||
quattro: () => saveStack()
|
||||
};
|
||||
|
||||
const frame = uno();
|
||||
|
||||
assertEq(frame.functionDisplayName, "tres.quattro");
|
||||
assertEq(frame.parent.functionDisplayName, "dos");
|
||||
assertEq(frame.parent.parent.functionDisplayName, "uno");
|
||||
assertEq(frame.parent.parent.parent.functionDisplayName, null);
|
||||
|
||||
assertEq(frame.parent.parent.parent.parent, null);
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
// Test that SavedFrame instances get removed from the SavedStacks frames cache
|
||||
// after a GC.
|
||||
|
||||
const FUZZ_FACTOR = 3;
|
||||
|
||||
function assertAboutEq(actual, expected) {
|
||||
if (Math.abs(actual - expected) > FUZZ_FACTOR)
|
||||
throw new Error("Assertion failed: expected about " + expected + ", got " + actual +
|
||||
". FUZZ_FACTOR = " + FUZZ_FACTOR);
|
||||
}
|
||||
|
||||
const stacks = [];
|
||||
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
stacks.push(saveStack());
|
||||
|
||||
assertAboutEq(getSavedFrameCount(), 50);
|
||||
|
||||
while (stacks.length) {
|
||||
stacks.pop();
|
||||
}
|
||||
gc();
|
||||
|
||||
stacks = null;
|
||||
gc();
|
||||
|
||||
assertAboutEq(getSavedFrameCount(), 0);
|
|
@ -1,15 +0,0 @@
|
|||
// Test that we can save stacks which have generator frames.
|
||||
|
||||
const { value: frame } = (function iife1() {
|
||||
return (function* generator() {
|
||||
yield (function iife2() {
|
||||
return saveStack();
|
||||
}());
|
||||
}()).next();
|
||||
}());
|
||||
|
||||
assertEq(frame.functionDisplayName, "iife2");
|
||||
assertEq(frame.parent.functionDisplayName, "generator");
|
||||
assertEq(frame.parent.parent.functionDisplayName, "iife1");
|
||||
assertEq(frame.parent.parent.parent.functionDisplayName, null);
|
||||
assertEq(frame.parent.parent.parent.parent, null);
|
|
@ -1,25 +0,0 @@
|
|||
// Test that we can save stacks with getter and setter function frames.
|
||||
|
||||
function assertStackLengthEq(stack, expectedLength) {
|
||||
let actual = 0;
|
||||
while (stack) {
|
||||
actual++;
|
||||
stack = stack.parent;
|
||||
}
|
||||
assertEq(actual, expectedLength);
|
||||
}
|
||||
|
||||
const get = { get s() { return saveStack(); } }.s;
|
||||
assertStackLengthEq(get, 2);
|
||||
|
||||
let set;
|
||||
try {
|
||||
({
|
||||
set s(v) {
|
||||
throw saveStack();
|
||||
}
|
||||
}).s = 1;
|
||||
} catch (s) {
|
||||
set = s;
|
||||
}
|
||||
assertStackLengthEq(set, 2);
|
|
@ -1,21 +0,0 @@
|
|||
// Test that you can't call the SavedFrame constructor and can only use
|
||||
// SavedFrame's getters on SavedFrame instances.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let proto = Object.getPrototypeOf(saveStack());
|
||||
|
||||
// Can't create new SavedFrame instances by hand.
|
||||
assertThrowsInstanceOf(() => {
|
||||
new proto.constructor();
|
||||
}, TypeError);
|
||||
|
||||
for (let p of ["source", "line", "column", "functionDisplayName", "parent"]) {
|
||||
// The getters shouldn't work on the prototype.
|
||||
assertThrowsInstanceOf(() => proto[p], TypeError);
|
||||
|
||||
// Nor should they work on random objects.
|
||||
let o = {};
|
||||
Object.defineProperty(o, p, Object.getOwnPropertyDescriptor(proto, p));
|
||||
assertThrowsInstanceOf(() => o[p], TypeError);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Test that we can save stacks with native code on the stack.
|
||||
|
||||
// Unlike Array.prototype.map, Array.prototype.filter is not self-hosted.
|
||||
const filter = (function iife() {
|
||||
try {
|
||||
[3].filter(n => { throw saveStack() });
|
||||
} catch (s) {
|
||||
return s;
|
||||
}
|
||||
}());
|
||||
|
||||
assertEq(filter.parent.functionDisplayName, "iife");
|
|
@ -1,70 +0,0 @@
|
|||
// Test that SavedFrame.prototype.parent gives the next older frame whose
|
||||
// principals are subsumed by the caller's principals.
|
||||
|
||||
// Given a string of letters |expected|, say "abc", assert that the stack
|
||||
// contains calls to a series of functions named by the next letter from
|
||||
// the string, say a, b, and then c. Younger frames appear earlier in
|
||||
// |expected| than older frames.
|
||||
function check(expected, stack) {
|
||||
print("check(" + uneval(expected) + ") against:\n" + stack);
|
||||
count++;
|
||||
|
||||
while (stack.length && expected.length) {
|
||||
assertEq(stack.shift(), expected[0]);
|
||||
expected = expected.slice(1);
|
||||
}
|
||||
|
||||
if (expected.length > 0) {
|
||||
throw new Error("Missing frames for: " + expected);
|
||||
}
|
||||
if (stack.length > 0 && !stack.every(s => s === null)) {
|
||||
throw new Error("Unexpected extra frame(s):\n" + stack);
|
||||
}
|
||||
}
|
||||
|
||||
// Go from a SavedFrame linked list to an array of function display names.
|
||||
function extract(stack) {
|
||||
const results = [];
|
||||
while (stack) {
|
||||
results.push(stack.functionDisplayName);
|
||||
stack = stack.parent;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const low = newGlobal({ principal: 0 });
|
||||
const mid = newGlobal({ principal: 0xffff });
|
||||
const high = newGlobal({ principal: 0xfffff });
|
||||
|
||||
var count = 0;
|
||||
|
||||
eval('function a() { check("a", extract(saveStack())); b(); }');
|
||||
low .eval('function b() { check("b", extract(saveStack())); c(); }');
|
||||
mid .eval('function c() { check("cba", extract(saveStack())); d(); }');
|
||||
high.eval('function d() { check("dcba", extract(saveStack())); e(); }');
|
||||
eval('function e() { check("edcba", extract(saveStack())); f(); }'); // no principal, so checks skipped
|
||||
low .eval('function f() { check("fb", extract(saveStack())); g(); }');
|
||||
mid .eval('function g() { check("gfecba", extract(saveStack())); h(); }');
|
||||
high.eval('function h() { check("hgfedcba", extract(saveStack())); }');
|
||||
|
||||
// Make everyone's functions visible to each other, as needed.
|
||||
b = low .b;
|
||||
low .c = mid .c;
|
||||
mid .d = high.d;
|
||||
high.e = e;
|
||||
f = low .f;
|
||||
low .g = mid .g;
|
||||
mid .h = high.h;
|
||||
|
||||
low.check = mid.check = high.check = check;
|
||||
|
||||
// They each must have their own extract so that CCWs don't mess up the
|
||||
// principals when we ask for the parent property.
|
||||
low. eval("" + extract);
|
||||
mid. eval("" + extract);
|
||||
high.eval("" + extract);
|
||||
|
||||
// Kick the whole process off.
|
||||
a();
|
||||
|
||||
assertEq(count, 8);
|
|
@ -1,56 +0,0 @@
|
|||
// Test that SavedFrame.prototype.toString only shows frames whose principal is
|
||||
// subsumed by the caller's principal.
|
||||
|
||||
var count = 0;
|
||||
|
||||
// Given a string of letters |expected|, say "abc", assert that the stack
|
||||
// contains calls to a series of functions named by the next letter from
|
||||
// the string, say a, b, and then c. Younger frames appear earlier in
|
||||
// |expected| than older frames.
|
||||
function check(expected, stack) {
|
||||
print("check(" + uneval(expected) + ") against:\n" + stack);
|
||||
count++;
|
||||
|
||||
// Extract only the function names from the stack trace. Omit the frames
|
||||
// for the top-level evaluation, if it is present.
|
||||
const frames = stack
|
||||
.split("\n")
|
||||
.filter(f => f.match(/^.@/))
|
||||
.map(f => f.replace(/@.*$/g, ""));
|
||||
|
||||
// Check the function names against the expected sequence.
|
||||
assertEq(frames.length, expected.length);
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEq(frames[i], expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var low = newGlobal({ principal: 0 });
|
||||
var mid = newGlobal({ principal: 0xffff });
|
||||
var high = newGlobal({ principal: 0xfffff });
|
||||
|
||||
eval('function a() { check("a", saveStack().toString()); b(); }');
|
||||
low .eval('function b() { check("b", saveStack().toString()); c(); }');
|
||||
mid .eval('function c() { check("cba", saveStack().toString()); d(); }');
|
||||
high.eval('function d() { check("dcba", saveStack().toString()); e(); }');
|
||||
eval('function e() { check("edcba", saveStack().toString()); f(); }'); // no principal, so checks skipped
|
||||
low .eval('function f() { check("fb", saveStack().toString()); g(); }');
|
||||
mid .eval('function g() { check("gfecba", saveStack().toString()); h(); }');
|
||||
high.eval('function h() { check("hgfedcba", saveStack().toString()); }');
|
||||
|
||||
// Make everyone's functions visible to each other, as needed.
|
||||
b = low .b;
|
||||
low .c = mid .c;
|
||||
mid .d = high.d;
|
||||
high.e = e;
|
||||
f = low .f;
|
||||
low .g = mid .g;
|
||||
mid .h = high.h;
|
||||
|
||||
low.check = mid.check = high.check = check;
|
||||
|
||||
// Kick the whole process off.
|
||||
a();
|
||||
|
||||
assertEq(count, 8);
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Test that we can save stacks with proxy handler frames.
|
||||
|
||||
const stack = (function iife() {
|
||||
return (new Proxy({}, {
|
||||
get: function get(t, n, r) { return saveStack(); }
|
||||
})).stack;
|
||||
}());
|
||||
|
||||
assertEq(stack.functionDisplayName, "get");
|
||||
assertEq(stack.parent.functionDisplayName, "iife");
|
|
@ -1,12 +0,0 @@
|
|||
// Test that the same saved stack is only ever allocated once.
|
||||
|
||||
const stacks = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
stacks.push(saveStack());
|
||||
}
|
||||
|
||||
const s = stacks.pop();
|
||||
for (let stack of stacks) {
|
||||
assertEq(s, stack);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// Test that we can save stacks with self-hosted function frames in them.
|
||||
|
||||
const map = (function () {
|
||||
return [3].map(n => saveStack()).pop();
|
||||
}());
|
||||
|
||||
assertEq(map.parent.functionDisplayName, "map");
|
||||
assertEq(map.parent.source, "self-hosted");
|
||||
|
||||
const reduce = (function () {
|
||||
return [3].reduce(() => saveStack(), 3);
|
||||
}());
|
||||
|
||||
assertEq(reduce.parent.functionDisplayName, "reduce");
|
||||
assertEq(reduce.parent.source, "self-hosted");
|
||||
|
||||
const forEach = (function () {
|
||||
try {
|
||||
[3].forEach(n => { throw saveStack() });
|
||||
} catch (s) {
|
||||
return s;
|
||||
}
|
||||
}());
|
||||
|
||||
assertEq(forEach.parent.functionDisplayName, "forEach");
|
||||
assertEq(forEach.parent.source, "self-hosted");
|
|
@ -1,19 +0,0 @@
|
|||
// Test that parent frames are shared when the older portions of two stacks are
|
||||
// the same.
|
||||
|
||||
let f1, f2;
|
||||
|
||||
function dos() {
|
||||
f1 = saveStack();
|
||||
f2 = saveStack();
|
||||
}
|
||||
|
||||
(function uno() {
|
||||
dos();
|
||||
}());
|
||||
|
||||
|
||||
// Different youngest frames.
|
||||
assertEq(f1 == f2, false);
|
||||
// However the parents should be the same.
|
||||
assertEq(f1.parent, f2.parent);
|
|
@ -1,8 +0,0 @@
|
|||
// Test that stringify'ing a saved frame with self-hosted parent frames doesn't
|
||||
// include the self-hosted parent frame in the output.
|
||||
|
||||
const map = (function () {
|
||||
return [3].map(n => saveStack()).pop();
|
||||
}());
|
||||
|
||||
assertEq(map.toString().contains("@self-hosted:"), false);
|
|
@ -113,9 +113,6 @@ JSCompartment::init(JSContext *cx)
|
|||
if (!enumerators)
|
||||
return false;
|
||||
|
||||
if (!savedStacks_.init())
|
||||
return false;
|
||||
|
||||
return debuggees.init(0);
|
||||
}
|
||||
|
||||
|
@ -566,7 +563,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
|||
sweepNewTypeObjectTable(newTypeObjects);
|
||||
sweepNewTypeObjectTable(lazyTypeObjects);
|
||||
sweepCallsiteClones();
|
||||
savedStacks_.sweep(rt);
|
||||
|
||||
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet()))
|
||||
global_ = nullptr;
|
||||
|
@ -669,8 +665,6 @@ JSCompartment::clearTables()
|
|||
newTypeObjects.clear();
|
||||
if (lazyTypeObjects.initialized())
|
||||
lazyTypeObjects.clear();
|
||||
if (savedStacks_.initialized())
|
||||
savedStacks_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -921,8 +915,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
|||
size_t *shapesCompartmentTables,
|
||||
size_t *crossCompartmentWrappersArg,
|
||||
size_t *regexpCompartment,
|
||||
size_t *debuggeesSet,
|
||||
size_t *savedStacksSet)
|
||||
size_t *debuggeesSet)
|
||||
{
|
||||
*compartmentObject += mallocSizeOf(this);
|
||||
types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
||||
|
@ -934,7 +927,6 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
|||
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
|
||||
*regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
|
||||
*debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf);
|
||||
*savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "gc/Zone.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/PIC.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -199,8 +198,6 @@ struct JSCompartment
|
|||
private:
|
||||
js::ObjectMetadataCallback objectMetadataCallback;
|
||||
|
||||
js::SavedStacks savedStacks_;
|
||||
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
public:
|
||||
|
@ -227,8 +224,7 @@ struct JSCompartment
|
|||
size_t *shapesCompartmentTables,
|
||||
size_t *crossCompartmentWrappers,
|
||||
size_t *regexpCompartment,
|
||||
size_t *debuggeesSet,
|
||||
size_t *savedStacksSet);
|
||||
size_t *debuggeesSet);
|
||||
|
||||
/*
|
||||
* Shared scope property tree, and arena-pool for allocating its nodes.
|
||||
|
@ -346,8 +342,6 @@ struct JSCompartment
|
|||
return objectMetadataCallback(cx, obj);
|
||||
}
|
||||
|
||||
js::SavedStacks &savedStacks() { return savedStacks_; }
|
||||
|
||||
void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
|
||||
|
||||
js::DtoaCache dtoaCache;
|
||||
|
|
|
@ -179,7 +179,6 @@ UNIFIED_SOURCES += [
|
|||
'vm/RegExpObject.cpp',
|
||||
'vm/RegExpStatics.cpp',
|
||||
'vm/Runtime.cpp',
|
||||
'vm/SavedStacks.cpp',
|
||||
'vm/ScopeObject.cpp',
|
||||
'vm/SelfHosting.cpp',
|
||||
'vm/Shape.cpp',
|
||||
|
|
|
@ -259,8 +259,7 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
|||
&cStats.shapesMallocHeapCompartmentTables,
|
||||
&cStats.crossCompartmentWrappersTable,
|
||||
&cStats.regexpCompartment,
|
||||
&cStats.debuggeesSet,
|
||||
&cStats.savedStacksSet);
|
||||
&cStats.debuggeesSet);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -1,512 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "vm/SavedStacks.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using mozilla::AddToHash;
|
||||
using mozilla::HashString;
|
||||
|
||||
namespace js {
|
||||
|
||||
/* static */ HashNumber
|
||||
SavedFrame::HashPolicy::hash(const Lookup &lookup)
|
||||
{
|
||||
return AddToHash(HashString(lookup.source->chars(), lookup.source->length()),
|
||||
lookup.line,
|
||||
lookup.column,
|
||||
lookup.functionDisplayName,
|
||||
SavedFramePtrHasher::hash(lookup.parent),
|
||||
JSPrincipalsPtrHasher::hash(lookup.principals));
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup)
|
||||
{
|
||||
if (existing->getLine() != lookup.line)
|
||||
return false;
|
||||
|
||||
if (existing->getColumn() != lookup.column)
|
||||
return false;
|
||||
|
||||
if (existing->getParent() != lookup.parent)
|
||||
return false;
|
||||
|
||||
if (existing->getPrincipals() != lookup.principals)
|
||||
return false;
|
||||
|
||||
JSAtom *source = existing->getSource();
|
||||
if (source->length() != lookup.source->length())
|
||||
return false;
|
||||
if (source != lookup.source)
|
||||
return false;
|
||||
|
||||
JSAtom *functionDisplayName = existing->getFunctionDisplayName();
|
||||
if (functionDisplayName) {
|
||||
if (!lookup.functionDisplayName)
|
||||
return false;
|
||||
if (functionDisplayName->length() != lookup.functionDisplayName->length())
|
||||
return false;
|
||||
if (0 != CompareAtoms(functionDisplayName, lookup.functionDisplayName))
|
||||
return false;
|
||||
} else if (lookup.functionDisplayName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SavedFrame::HashPolicy::rekey(Key &key, const Key &newKey)
|
||||
{
|
||||
key = newKey;
|
||||
}
|
||||
|
||||
/* static */ const Class SavedFrame::class_ = {
|
||||
"SavedFrame",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT),
|
||||
|
||||
JS_PropertyStub, // addProperty
|
||||
JS_DeletePropertyStub, // delProperty
|
||||
JS_PropertyStub, // getProperty
|
||||
JS_StrictPropertyStub, // setProperty
|
||||
JS_EnumerateStub, // enumerate
|
||||
JS_ResolveStub, // resolve
|
||||
JS_ConvertStub, // convert
|
||||
|
||||
SavedFrame::finalize // finalize
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
SavedFrame::finalize(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
JSPrincipals *p = obj->as<SavedFrame>().getPrincipals();
|
||||
if (p) {
|
||||
JSRuntime *rt = obj->runtimeFromMainThread();
|
||||
JS_DropPrincipals(rt, p);
|
||||
}
|
||||
}
|
||||
|
||||
JSAtom *
|
||||
SavedFrame::getSource()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_SOURCE);
|
||||
JSString *s = v.toString();
|
||||
return &s->asAtom();
|
||||
}
|
||||
|
||||
size_t
|
||||
SavedFrame::getLine()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_LINE);
|
||||
return v.toInt32();
|
||||
}
|
||||
|
||||
size_t
|
||||
SavedFrame::getColumn()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_COLUMN);
|
||||
return v.toInt32();
|
||||
}
|
||||
|
||||
JSAtom *
|
||||
SavedFrame::getFunctionDisplayName()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME);
|
||||
if (v.isNull())
|
||||
return nullptr;
|
||||
JSString *s = v.toString();
|
||||
return &s->asAtom();
|
||||
}
|
||||
|
||||
SavedFrame *
|
||||
SavedFrame::getParent()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_PARENT);
|
||||
return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
|
||||
}
|
||||
|
||||
JSPrincipals *
|
||||
SavedFrame::getPrincipals()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_PRINCIPALS);
|
||||
if (v.isUndefined())
|
||||
return nullptr;
|
||||
return static_cast<JSPrincipals *>(v.toPrivate());
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initFromLookup(Lookup &lookup)
|
||||
{
|
||||
JS_ASSERT(lookup.source);
|
||||
JS_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined());
|
||||
setReservedSlot(JSSLOT_SOURCE, StringValue(lookup.source));
|
||||
|
||||
setReservedSlot(JSSLOT_LINE, NumberValue(lookup.line));
|
||||
setReservedSlot(JSSLOT_COLUMN, NumberValue(lookup.column));
|
||||
setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME,
|
||||
lookup.functionDisplayName
|
||||
? StringValue(lookup.functionDisplayName)
|
||||
: NullValue());
|
||||
setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup.parent));
|
||||
setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(lookup.parent));
|
||||
|
||||
JS_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined());
|
||||
if (lookup.principals)
|
||||
JS_HoldPrincipals(lookup.principals);
|
||||
setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup.principals));
|
||||
}
|
||||
|
||||
bool
|
||||
SavedFrame::parentMoved()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_PRIVATE_PARENT);
|
||||
JSObject *p = static_cast<JSObject *>(v.toPrivate());
|
||||
return p == getParent();
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::updatePrivateParent()
|
||||
{
|
||||
setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(getParent()));
|
||||
}
|
||||
|
||||
bool
|
||||
SavedFrame::isSelfHosted()
|
||||
{
|
||||
JSAtom *source = getSource();
|
||||
return StringEqualsAscii(source, "self-hosted");
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
|
||||
"SavedFrame");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ SavedFrame *
|
||||
SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
||||
{
|
||||
const Value &thisValue = args.thisv();
|
||||
|
||||
if (!thisValue.isObject()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject &thisObject = thisValue.toObject();
|
||||
if (!thisObject.is<SavedFrame>()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
SavedFrame::class_.name, fnName, thisObject.getClass()->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check for SavedFrame.prototype, which has the same class as SavedFrame
|
||||
// instances, however doesn't actually represent a captured stack frame. It
|
||||
// is the only object that is<SavedFrame>() but doesn't have a source.
|
||||
if (thisObject.getReservedSlot(JSSLOT_SOURCE).isNull()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
SavedFrame::class_.name, fnName, "prototype object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &thisObject.as<SavedFrame>();
|
||||
}
|
||||
|
||||
// Get the SavedFrame * from the current this value and handle any errors that
|
||||
// might occur therein.
|
||||
//
|
||||
// These parameters must already exist when calling this macro:
|
||||
// - JSContext *cx
|
||||
// - unsigned argc
|
||||
// - Value *vp
|
||||
// - const char *fnName
|
||||
// These parameters will be defined after calling this macro:
|
||||
// - CallArgs args
|
||||
// - Rooted<SavedFrame *> frame (will be non-null)
|
||||
#define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
Rooted<SavedFrame *> frame(cx, checkThis(cx, args, fnName)); \
|
||||
if (!frame) \
|
||||
return false
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
|
||||
args.rval().setString(frame->getSource());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
|
||||
uint32_t line = frame->getLine();
|
||||
args.rval().setNumber(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
|
||||
uint32_t column = frame->getColumn();
|
||||
args.rval().setNumber(column);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
|
||||
RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if (name)
|
||||
args.rval().setString(name);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
|
||||
do
|
||||
frame = frame->getParent();
|
||||
while (frame && principals && subsumes &&
|
||||
!subsumes(principals, frame->getPrincipals()));
|
||||
|
||||
args.rval().setObjectOrNull(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ const JSPropertySpec SavedFrame::properties[] = {
|
||||
JS_PSG("source", SavedFrame::sourceProperty, 0),
|
||||
JS_PSG("line", SavedFrame::lineProperty, 0),
|
||||
JS_PSG("column", SavedFrame::columnProperty, 0),
|
||||
JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
|
||||
JS_PSG("parent", SavedFrame::parentProperty, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
|
||||
StringBuffer sb(cx);
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
|
||||
do {
|
||||
if (principals && subsumes && !subsumes(principals, frame->getPrincipals()))
|
||||
continue;
|
||||
if (frame->isSelfHosted())
|
||||
continue;
|
||||
|
||||
RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if ((name && !sb.append(name))
|
||||
|| !sb.append('@')
|
||||
|| !sb.append(frame->getSource())
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
|
||||
|| !sb.append(':')
|
||||
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
|
||||
|| !sb.append('\n')) {
|
||||
return false;
|
||||
}
|
||||
} while ((frame = frame->getParent()));
|
||||
|
||||
args.rval().setString(sb.finishString());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ const JSFunctionSpec SavedFrame::methods[] = {
|
||||
JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
bool
|
||||
SavedStacks::init()
|
||||
{
|
||||
return frames.init();
|
||||
}
|
||||
|
||||
bool
|
||||
SavedStacks::saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame)
|
||||
{
|
||||
JS_ASSERT(initialized());
|
||||
JS_ASSERT(&cx->compartment()->savedStacks() == this);
|
||||
|
||||
ScriptFrameIter iter(cx);
|
||||
return insertFrames(cx, iter, frame);
|
||||
}
|
||||
|
||||
void
|
||||
SavedStacks::sweep(JSRuntime *rt)
|
||||
{
|
||||
if (frames.initialized()) {
|
||||
for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) {
|
||||
JSObject *obj = static_cast<JSObject *>(e.front());
|
||||
JSObject *temp = obj;
|
||||
|
||||
if (IsObjectAboutToBeFinalized(&obj)) {
|
||||
e.removeFront();
|
||||
} else {
|
||||
SavedFrame *frame = &obj->as<SavedFrame>();
|
||||
bool parentMoved = frame->parentMoved();
|
||||
|
||||
if (parentMoved) {
|
||||
frame->updatePrivateParent();
|
||||
}
|
||||
|
||||
if (obj != temp || parentMoved) {
|
||||
Rooted<SavedFrame*> parent(rt, frame->getParent());
|
||||
e.rekeyFront(SavedFrame::Lookup(frame->getSource(),
|
||||
frame->getLine(),
|
||||
frame->getColumn(),
|
||||
frame->getFunctionDisplayName(),
|
||||
parent,
|
||||
frame->getPrincipals()),
|
||||
frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) {
|
||||
savedFrameProto = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SavedStacks::count()
|
||||
{
|
||||
JS_ASSERT(initialized());
|
||||
return frames.count();
|
||||
}
|
||||
|
||||
void
|
||||
SavedStacks::clear()
|
||||
{
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
size_t
|
||||
SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
return frames.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
bool
|
||||
SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame)
|
||||
{
|
||||
if (iter.done()) {
|
||||
frame.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
ScriptFrameIter thisFrame(iter);
|
||||
Rooted<SavedFrame*> parentFrame(cx);
|
||||
if (!insertFrames(cx, ++iter, &parentFrame))
|
||||
return false;
|
||||
|
||||
RootedScript script(cx, thisFrame.script());
|
||||
RootedFunction callee(cx, thisFrame.maybeCallee());
|
||||
const char *filename = script->filename();
|
||||
RootedAtom source(cx, Atomize(cx, filename, strlen(filename)));
|
||||
if (!source)
|
||||
return false;
|
||||
uint32_t column;
|
||||
uint32_t line = PCToLineNumber(script, thisFrame.pc(), &column);
|
||||
|
||||
SavedFrame::Lookup lookup(source,
|
||||
line,
|
||||
column,
|
||||
callee ? callee->displayAtom() : nullptr,
|
||||
parentFrame,
|
||||
thisFrame.compartment()->principals);
|
||||
|
||||
frame.set(getOrCreateSavedFrame(cx, lookup));
|
||||
return frame.address() != nullptr;
|
||||
}
|
||||
|
||||
SavedFrame *
|
||||
SavedStacks::getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup)
|
||||
{
|
||||
SavedFrame::Set::AddPtr p = frames.lookupForAdd(lookup);
|
||||
if (p)
|
||||
return *p;
|
||||
|
||||
Rooted<SavedFrame *> frame(cx, createFrameFromLookup(cx, lookup));
|
||||
if (!frame)
|
||||
return nullptr;
|
||||
|
||||
if (!frames.relookupOrAdd(p, lookup, frame))
|
||||
return nullptr;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
SavedStacks::getOrCreateSavedFramePrototype(JSContext *cx)
|
||||
{
|
||||
if (savedFrameProto)
|
||||
return savedFrameProto;
|
||||
|
||||
Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal());
|
||||
if (!global)
|
||||
return nullptr;
|
||||
|
||||
savedFrameProto = js_InitClass(cx, global, global->getOrCreateObjectPrototype(cx),
|
||||
&SavedFrame::class_, SavedFrame::construct, 0,
|
||||
SavedFrame::properties, SavedFrame::methods, nullptr, nullptr);
|
||||
// The only object with the SavedFrame::class_ that doesn't have a source
|
||||
// should be the prototype.
|
||||
savedFrameProto->setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
|
||||
return savedFrameProto;
|
||||
}
|
||||
|
||||
SavedFrame *
|
||||
SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup)
|
||||
{
|
||||
RootedObject proto(cx, getOrCreateSavedFramePrototype(cx));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
JS_ASSERT(proto->compartment() == cx->compartment());
|
||||
|
||||
RootedObject global(cx, cx->compartment()->maybeGlobal());
|
||||
if (!global)
|
||||
return nullptr;
|
||||
|
||||
JS_ASSERT(global->compartment() == cx->compartment());
|
||||
|
||||
RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global));
|
||||
if (!frameObj)
|
||||
return nullptr;
|
||||
|
||||
SavedFrame &f = frameObj->as<SavedFrame>();
|
||||
f.initFromLookup(lookup);
|
||||
|
||||
return &f;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
|
@ -1,144 +0,0 @@
|
|||
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef vm_SavedStacks_h
|
||||
#define vm_SavedStacks_h
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "vm/Stack.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class SavedFrame : public JSObject {
|
||||
friend class SavedStacks;
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
static void finalize(FreeOp *fop, JSObject *obj);
|
||||
|
||||
// Prototype methods and properties to be exposed to JS.
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool sourceProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool lineProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool columnProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool parentProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool toStringMethod(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
// Convenient getters for SavedFrame's reserved slots for use from C++.
|
||||
JSAtom *getSource();
|
||||
size_t getLine();
|
||||
size_t getColumn();
|
||||
JSAtom *getFunctionDisplayName();
|
||||
SavedFrame *getParent();
|
||||
JSPrincipals *getPrincipals();
|
||||
|
||||
bool isSelfHosted();
|
||||
|
||||
struct Lookup;
|
||||
struct HashPolicy;
|
||||
|
||||
typedef HashSet<SavedFrame *,
|
||||
HashPolicy,
|
||||
SystemAllocPolicy> Set;
|
||||
|
||||
private:
|
||||
void initFromLookup(Lookup &lookup);
|
||||
|
||||
enum {
|
||||
// The reserved slots in the SavedFrame class.
|
||||
JSSLOT_SOURCE,
|
||||
JSSLOT_LINE,
|
||||
JSSLOT_COLUMN,
|
||||
JSSLOT_FUNCTIONDISPLAYNAME,
|
||||
JSSLOT_PARENT,
|
||||
JSSLOT_PRINCIPALS,
|
||||
JSSLOT_PRIVATE_PARENT,
|
||||
|
||||
// The total number of reserved slots in the SavedFrame class.
|
||||
JSSLOT_COUNT
|
||||
};
|
||||
|
||||
// Because we hash the parent pointer, we need to rekey a saved frame
|
||||
// whenever its parent was relocated by the GC. However, the GC doesn't
|
||||
// notify us when this occurs. As a work around, we keep a duplicate copy of
|
||||
// the parent pointer as a private value in a reserved slot. Whenever the
|
||||
// private value parent pointer doesn't match the regular parent pointer, we
|
||||
// know that GC moved the parent and we need to update our private value and
|
||||
// rekey the saved frame in its hash set. These two methods are helpers for
|
||||
// this process.
|
||||
bool parentMoved();
|
||||
void updatePrivateParent();
|
||||
|
||||
static SavedFrame *checkThis(JSContext *cx, CallArgs &args, const char *fnName);
|
||||
};
|
||||
|
||||
struct SavedFrame::Lookup {
|
||||
Lookup(JSAtom *source, size_t line, size_t column, JSAtom *functionDisplayName,
|
||||
Handle<SavedFrame*> parent, JSPrincipals *principals)
|
||||
: source(source),
|
||||
line(line),
|
||||
column(column),
|
||||
functionDisplayName(functionDisplayName),
|
||||
parent(parent),
|
||||
principals(principals)
|
||||
{
|
||||
JS_ASSERT(source);
|
||||
}
|
||||
|
||||
JSAtom *source;
|
||||
size_t line;
|
||||
size_t column;
|
||||
JSAtom *functionDisplayName;
|
||||
Handle<SavedFrame*> parent;
|
||||
JSPrincipals *principals;
|
||||
};
|
||||
|
||||
struct SavedFrame::HashPolicy
|
||||
{
|
||||
typedef SavedFrame::Lookup Lookup;
|
||||
typedef PointerHasher<SavedFrame *, 3> SavedFramePtrHasher;
|
||||
typedef PointerHasher<JSPrincipals *, 3> JSPrincipalsPtrHasher;
|
||||
|
||||
static HashNumber hash(const Lookup &lookup);
|
||||
static bool match(SavedFrame *existing, const Lookup &lookup);
|
||||
|
||||
typedef SavedFrame* Key;
|
||||
static void rekey(Key &key, const Key &newKey);
|
||||
};
|
||||
|
||||
class SavedStacks {
|
||||
public:
|
||||
SavedStacks() : frames(), savedFrameProto(nullptr) { }
|
||||
|
||||
bool init();
|
||||
bool initialized() const { return frames.initialized(); }
|
||||
bool saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame);
|
||||
void sweep(JSRuntime *rt);
|
||||
uint32_t count();
|
||||
void clear();
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
private:
|
||||
SavedFrame::Set frames;
|
||||
JSObject *savedFrameProto;
|
||||
|
||||
bool insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame);
|
||||
SavedFrame *getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup);
|
||||
// |SavedFrame.prototype| is created lazily and held weakly. It should only
|
||||
// be accessed through this method.
|
||||
JSObject *getOrCreateSavedFramePrototype(JSContext *cx);
|
||||
SavedFrame *createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_SavedStacks_h */
|
|
@ -677,7 +677,7 @@ FrameIter::FrameIter(const FrameIter &other)
|
|||
: data_(other.data_)
|
||||
#ifdef JS_ION
|
||||
, ionInlineFrames_(other.data_.cx_,
|
||||
data_.jitFrames_.isIonJS() ? &other.ionInlineFrames_ : nullptr)
|
||||
data_.jitFrames_.isScripted() ? &other.ionInlineFrames_ : nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче