// If we never see a matching frame when requesting a truncated // stack we should return the empty string function test_no_match() {
let obj = {}; // test_elision chosen arbitrarily as a function object which // doesn't exist in the call stack here.
let capture = () => Error.captureStackTrace(obj, test_elision);
nestedLambda(capture);
assertEq(obj.stack, "");
}
test_no_match()
function count_frames(str) { return str.split("\n").length
}
function test_nofilter() {
let obj = {};
let capture = () => Error.captureStackTrace(obj);
nestedLambda(capture);
assertEq(count_frames(obj.stack), 9);
}
test_nofilter();
function test_in_eval() {
let obj = eval(`
let obj = {};
let capture = () => Error.captureStackTrace(obj);
nestedLambda(capture);
obj
`)
// Same as above, with an eval frame added!
assertEq(count_frames(obj.stack), 10);
}
test_in_eval();
function test_uncensored() {
let err = undefined; function create_err() {
err = new Error;
Error.captureStackTrace(err, test_uncensored);
}
nestedLambda(create_err);
// Calling Error.captureStackTrace doesn't mess with the internal // [[ErrorData]] slot
assertEq(count_frames(err.stack), 2);
assertEq(count_frames(getStack(err)), 9)
}
test_uncensored()
// In general, the stacks a non-caller version of Error.captureStackStrace // should match what Error gives you function compare_stacks() { function censor_column(str) { return str.replace(/:(\d+):\d+\n/g, ":$1:censored\n")
}
let obj = {};
let err = (Error.captureStackTrace(obj), new Error)
assertEq(censor_column(err.stack), censor_column(obj.stack));
}
compare_stacks();
nestedLambda(compare_stacks)
// New global
function test_in_global(global) {
global.evaluate(caller.toString());
global.evaluate(fill.toString());
global.evaluate(test_elision.toString());
global.evaluate("test_elision()");
let global2 = newGlobal({ principal: 0 });
test_in_global(global2)
let global3 = newGlobal({ principal: 0xfffff });
test_in_global(global3)
// What if the caller is a proxy? const caller_proxy = new Proxy(caller, {
apply: function (target, thisArg, arguments) { return target(...arguments);
}
});
function fill_proxy() {
let x = {}
Error.captureStackTrace(x, caller_proxy); return x;
}
// Proxies don't count for elision. function test_proxy_elision() {
let x = caller_proxy(fill_proxy);
let { stack } = x;
assertEq(stack.includes("caller"), true);
assertEq(stack.includes("fill_proxy"), true);
}
test_proxy_elision();
const trivial_proxy = new Proxy(caller, {}); function fill_trivial() {
let x = {}
Error.captureStackTrace(x, trivial_proxy); return x;
}
// Elision doesn't work even on forwarding proxy function test_trivial_elision() {
let x = caller(fill_trivial);
let { stack } = x;
assertEq(stack.includes("caller"), true);
assertEq(stack.includes("fill"), true);
}
test_trivial_elision();
// Elision happens through bind function test_bind_elision() {
let b = caller.bind(undefined, fill);
let { stack } = b();
assertEq(stack.includes("caller"), false);
assertEq(stack.includes("fill"), false);
}
test_bind_elision();
// Cross Realm testing
let nr = newGlobal({ newCompartment: true })
nr.eval(`globalThis.x = {}`);
Error.captureStackTrace(nr.x);
// Test strict definition function test_strict_definition() { "use strict";
assertThrowsInstanceOf(() => Error.captureStackTrace(Object.freeze({ stack: null })), TypeError);
}
test_strict_definition();
function test_property_descriptor() {
let o = {};
Error.captureStackTrace(o);
let desc = Object.getOwnPropertyDescriptor(o, "stack");
assertEq(desc.configurable, true)
assertEq(desc.writable, true)
assertEq(desc.enumerable, false)
}
test_property_descriptor();
function test_delete() {
let o = {};
Error.captureStackTrace(o); delete o.stack
assertEq("stack" in o, false)
}
test_delete();
// Principal testing: This is basic/shell-principals.js extended to support // and compare Error.captureStackTrace. // // Reminder: // > In the shell, a principal is simply a 32-bit mask: P subsumes Q if the // > set bits in P are a superset of those in Q. Thus, the principal 0 is // > subsumed by everything, and the principal ~0 subsumes everything.
// 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.
let count = 0; function check(expected, stack) {
print("check(" + JSON.stringify(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. var split = stack.split(/(.)?@.*\n/).slice(0, -1); if (split[split.length - 1] === undefined)
split = split.slice(0, -2);
print(JSON.stringify(split)); // Check the function names against the expected sequence.
assertEq(split.length, expected.length * 2); for (var i = 0; i < expected.length; i++)
assertEq(split[i * 2 + 1], expected[i]);
}
var low = newGlobal({ principal: 0 }); var mid = newGlobal({ principal: 0xffff }); var high = newGlobal({ principal: 0xfffff });
eval('function a() { let o = {}; Error.captureStackTrace(o); check("a", o.stack); b(); }');
low.eval('function b() { let o = {}; Error.captureStackTrace(o); check("b", o.stack); c(); }');
mid.eval('function c() { let o = {}; Error.captureStackTrace(o); check("cba", o.stack); d(); }');
high.eval('function d() { let o = {}; Error.captureStackTrace(o); check("dcba", o.stack); e(); }');
// Globals created with no explicit principals get 0xffff.
eval('function e() { let o = {}; Error.captureStackTrace(o); check("ecba", o.stack); f(); }');
low.eval('function f() { let o = {}; Error.captureStackTrace(o); check("fb", o.stack); g(); }');
mid.eval('function g() { let o = {}; Error.captureStackTrace(o); check("gfecba", o.stack); h(); }');
high.eval('function h() { let o = {}; Error.captureStackTrace(o); check("hgfedcba", o.stack); }');
// 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);
// Ensure filtering is based on caller realm not on target object.
low.eval("low_target = {}");
mid.eval("mid_target = {}");
high.eval("high_target = {}");
// Caller function z is from high, but using low Error.captureStackTrace, so // z should be elided.
high.eval("z(q)");
check("q", low.low_target.stack);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.