diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit-test/tests/profiler | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/profiler')
31 files changed, 481 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js new file mode 100644 index 0000000000..c64a121b66 --- /dev/null +++ b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js @@ -0,0 +1,50 @@ +// AutoEntryMonitor should catch all entry points into JavaScript. + +load(libdir + 'array-compare.js'); + +function cold_and_warm(f, params, expected) { + print(JSON.stringify(params)); + print(JSON.stringify(entryPoints(params))); + assertEq(arraysEqual(entryPoints(params), expected), true); + + // Warm up the function a bit, so the JITs will compile it, and try again. + if (f) + for (i = 0; i < 10; i++) + f(); + + assertEq(arraysEqual(entryPoints(params), expected), true); +} + +function entry1() { } +cold_and_warm(entry1, { function: entry1 }, [ "entry1" ]); + +var getx = { get x() { } }; +cold_and_warm(Object.getOwnPropertyDescriptor(getx, 'x').get, + { object: getx, property: 'x' }, [ "get x" ]); + +var sety = { set y(v) { } }; +cold_and_warm(Object.getOwnPropertyDescriptor(sety, 'y').set, + { object: sety, property: 'y', value: 'glerk' }, [ "set y" ]); + +cold_and_warm(Object.prototype.toString, { ToString: {} }, []); + +var toS = { toString: function myToString() { return "string"; } }; +cold_and_warm(toS.toString, { ToString: toS }, [ "myToString" ]); + +cold_and_warm(undefined, { ToNumber: 5 }, []); + +var vOf = { valueOf: function myValueOf() { return 42; } }; +cold_and_warm(vOf.valueOf, { ToNumber: vOf }, [ "myValueOf" ]); + +var toSvOf = { toString: function relations() { return Object; }, + valueOf: function wallpaper() { return 17; } }; +cold_and_warm(() => { toSvOf.toString(); toSvOf.valueOf(); }, + { ToString: toSvOf }, [ "relations", "wallpaper" ]); + +var vOftoS = { toString: function ettes() { return "6-inch lengths"; }, + valueOf: function deathBy() { return Math; } }; +cold_and_warm(() => { vOftoS.valueOf(); vOftoS.toString(); }, + { ToNumber: vOftoS }, [ "deathBy", "ettes" ]); + + +cold_and_warm(() => 0, { eval: "Math.atan2(1,1);" }, [ "eval:entryPoint eval" ]); diff --git a/js/src/jit-test/tests/profiler/AutoEntryMonitor-02.js b/js/src/jit-test/tests/profiler/AutoEntryMonitor-02.js new file mode 100644 index 0000000000..1aee82068c --- /dev/null +++ b/js/src/jit-test/tests/profiler/AutoEntryMonitor-02.js @@ -0,0 +1,12 @@ +// Nested uses of AutoEntryMonitor should behave with decorum. + +load(libdir + 'array-compare.js'); + +function Cobb() { + var twoShot = { toString: function Saito() { return Object; }, + valueOf: function Fischer() { return "Ariadne"; } }; + assertEq(arraysEqual(entryPoints({ ToString: twoShot }), + [ "Saito", "Fischer" ]), true); +} + +assertEq(arraysEqual(entryPoints({ function: Cobb }), ["Cobb"]), true); diff --git a/js/src/jit-test/tests/profiler/bug1135703.js b/js/src/jit-test/tests/profiler/bug1135703.js new file mode 100644 index 0000000000..3bad17e943 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1135703.js @@ -0,0 +1,6 @@ + +for (var idx = 0; idx < 20; ++idx) { + newFunc("enableGeckoProfilingWithSlowAssertions(); disableGeckoProfiling();"); +} +newFunc("enableGeckoProfiling();"); +function newFunc(x) { new Function(x)(); }; diff --git a/js/src/jit-test/tests/profiler/bug1161351.js b/js/src/jit-test/tests/profiler/bug1161351.js new file mode 100644 index 0000000000..2da402275b --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1161351.js @@ -0,0 +1,16 @@ +function x() { n; } +function f() { + try { x(); } catch(ex) {} +} +var g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () {};"); +enableGeckoProfiling(); +try { + enableSingleStepProfiling(); +} catch (e) { + // Not all platforms support single-step profiling. +} +f(); +f(); +f(); diff --git a/js/src/jit-test/tests/profiler/bug1164448.js b/js/src/jit-test/tests/profiler/bug1164448.js new file mode 100644 index 0000000000..23147742f1 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1164448.js @@ -0,0 +1,26 @@ +// |jit-test| error: TypeError + +print = function(s) { return s.toString(); } +var gTestcases = new Array(); +function TestCase(n, d, e, a) { + return gTestcases[gTc++] = this; +} + dump = print; + for ( gTc=0; gTc < gTestcases.length; gTc++ ) {} +function jsTestDriverEnd() { + for (var i = 0; i < gTestcases.length; i++) + gTestcases[i].dump(); +} +TestCase(); +var g = newGlobal(); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () {};"); +enableGeckoProfiling(); +if (getBuildConfiguration()["arm-simulator"]) + enableSingleStepProfiling(1); +loadFile("jsTestDriverEnd();"); +loadFile("jsTestDriverEnd();"); +jsTestDriverEnd(); +function loadFile(lfVarx) { + try { evaluate(lfVarx); } catch (lfVare) {} +} diff --git a/js/src/jit-test/tests/profiler/bug1211962.js b/js/src/jit-test/tests/profiler/bug1211962.js new file mode 100644 index 0000000000..e18f094ce1 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1211962.js @@ -0,0 +1,10 @@ +// |jit-test| slow; skip-if: !('oomTest' in this) || helperThreadCount() === 0 + +enableGeckoProfiling(); +var lfGlobal = newGlobal(); +for (lfLocal in this) { + lfGlobal[lfLocal] = this[lfLocal]; +} +const script = 'oomTest(() => getBacktrace({args: true, locals: "123795", thisprops: true}));'; +lfGlobal.offThreadCompileScript(script); +lfGlobal.runOffThreadScript(); diff --git a/js/src/jit-test/tests/profiler/bug1231925.js b/js/src/jit-test/tests/profiler/bug1231925.js new file mode 100644 index 0000000000..87325b6763 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1231925.js @@ -0,0 +1,6 @@ +// |jit-test| skip-if: !('oomTest' in this) + +enableGeckoProfiling(); +oomTest(function() { + eval("(function() {})()") +}); diff --git a/js/src/jit-test/tests/profiler/bug1233921.js b/js/src/jit-test/tests/profiler/bug1233921.js new file mode 100644 index 0000000000..18cce796be --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1233921.js @@ -0,0 +1,19 @@ +g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () {}"); +enableGeckoProfiling(); +try { + enableSingleStepProfiling(); +} catch(e) { + quit(); +} +f(); +f(); +function $ERROR() { + throw Error; +} +function f() { + try { + $ERROR() + } catch (ex) {} +} diff --git a/js/src/jit-test/tests/profiler/bug1242840.js b/js/src/jit-test/tests/profiler/bug1242840.js new file mode 100644 index 0000000000..8770403409 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1242840.js @@ -0,0 +1,15 @@ +// |jit-test| skip-if: !('oomTest' in this) + +enableGeckoProfiling(); +oomTest(() => { + try { + for (quit of ArrayBuffer); + } catch (e) { + switch (1) { + case 0: + let x + case 1: + (function() { return x; })() + } + } +}) diff --git a/js/src/jit-test/tests/profiler/bug1261324.js b/js/src/jit-test/tests/profiler/bug1261324.js new file mode 100644 index 0000000000..053f00928e --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1261324.js @@ -0,0 +1,24 @@ +g = newGlobal({newCompartment: true}) +g.parent = this +g.eval("new Debugger(parent).onExceptionUnwind = function () {}") +enableGeckoProfiling() + +try { + // Only the ARM simulator supports single step profiling. + enableSingleStepProfiling(); +} catch (e) { + quit(); +} + +function assertThrowsInstanceOf(f) { + try { + f() + } catch (exc) {} +} +function testThrow(thunk) { + for (i = 0; i < 20; i++) { + iter = thunk() + assertThrowsInstanceOf(function() { return iter.throw(); }) + } +} +testThrow(function*() {}) diff --git a/js/src/jit-test/tests/profiler/bug1352507-1.js b/js/src/jit-test/tests/profiler/bug1352507-1.js new file mode 100644 index 0000000000..540c4d5350 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1352507-1.js @@ -0,0 +1,3 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +evalInWorker("enableGeckoProfiling()"); diff --git a/js/src/jit-test/tests/profiler/bug1427774.js b/js/src/jit-test/tests/profiler/bug1427774.js new file mode 100644 index 0000000000..53a3103f3b --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1427774.js @@ -0,0 +1,29 @@ +setJitCompilerOption("baseline.warmup.trigger", 0); +enableGeckoProfiling(); +try { + enableSingleStepProfiling(); +} catch(e) { + quit(); +} +function removeAdd(dbg, g) { + dbg.removeDebuggee(g); +} +function newGlobalDebuggerPair(toggleSeq) { + var g = newGlobal({newCompartment: true}); + var dbg = new Debugger; + dbg.addDebuggee(g); + g.eval("" + function f() {return 100}); + return [g, dbg]; +} +function testTrap(toggleSeq) { + var [g, dbg] = newGlobalDebuggerPair(toggleSeq); + dbg.onEnterFrame = function(f) { + f.script.setBreakpoint(Symbol.iterator == (this) ^ (this), { + hit: function() { + toggleSeq(dbg, g); + } + }); + }; + assertEq(g.f(), 100); +} +testTrap(removeAdd); diff --git a/js/src/jit-test/tests/profiler/bug1478509.js b/js/src/jit-test/tests/profiler/bug1478509.js new file mode 100644 index 0000000000..eafe5a70b5 --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1478509.js @@ -0,0 +1,5 @@ +// |jit-test| error: SyntaxError +enableGeckoProfiling(); +s = newGlobal(); +evalcx("let x;", s); +evalcx("let x;", s); diff --git a/js/src/jit-test/tests/profiler/bug1502744.js b/js/src/jit-test/tests/profiler/bug1502744.js new file mode 100644 index 0000000000..c0f1ee3dfd --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1502744.js @@ -0,0 +1,20 @@ +// |jit-test| error:ReferenceError +(function(global) { + global.makeIterator = function makeIterator(overrides) { + var iterator = { + return: function(x) { + return overrides.ret(x); + } + }; + return function() { + return iterator; + }; + } +})(this); +var iterable = {}; +iterable[Symbol.iterator] = makeIterator({ + ret: (function() { + enableGeckoProfilingWithSlowAssertions(); + }) +}); +0, [...{} [throwlhs()]] = iterable; diff --git a/js/src/jit-test/tests/profiler/bug1563889.js b/js/src/jit-test/tests/profiler/bug1563889.js new file mode 100644 index 0000000000..c8f9776ada --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug1563889.js @@ -0,0 +1,3 @@ +// |jit-test| skip-if: !('oomTest' in this) +for (var i = 0; i < 20; i++) {} +oomTest(enableGeckoProfiling); diff --git a/js/src/jit-test/tests/profiler/bug925309.js b/js/src/jit-test/tests/profiler/bug925309.js new file mode 100644 index 0000000000..9301cb74ae --- /dev/null +++ b/js/src/jit-test/tests/profiler/bug925309.js @@ -0,0 +1,2 @@ + +for(var i = 0; i < 100; enableGeckoProfiling(), i++) {} diff --git a/js/src/jit-test/tests/profiler/debugmode-osr-exception-return-addr.js b/js/src/jit-test/tests/profiler/debugmode-osr-exception-return-addr.js new file mode 100644 index 0000000000..f91ce610e5 --- /dev/null +++ b/js/src/jit-test/tests/profiler/debugmode-osr-exception-return-addr.js @@ -0,0 +1,16 @@ +// |jit-test| error: ReferenceError + +var g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("new Debugger(parent).onExceptionUnwind = function () { };"); +enableGeckoProfiling(); + +try { + // Only the ARM simulator supports single step profiling. + enableSingleStepProfiling(); +} catch (e) { + throw new ReferenceError; +} + +enableSingleStepProfiling(); +a() diff --git a/js/src/jit-test/tests/profiler/debugmode-osr-resume-addr.js b/js/src/jit-test/tests/profiler/debugmode-osr-resume-addr.js new file mode 100644 index 0000000000..0246234932 --- /dev/null +++ b/js/src/jit-test/tests/profiler/debugmode-osr-resume-addr.js @@ -0,0 +1,11 @@ +enableGeckoProfiling(); +try { + // Only the ARM simulator supports single step profiling. + enableSingleStepProfiling(); +} catch (e) { + quit(0); +} +var g = newGlobal({newCompartment: true}); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) {}; +g.eval("var line = new Error().lineNumber; debugger;"); diff --git a/js/src/jit-test/tests/profiler/enterjit-osr-disabling-earlyret.js b/js/src/jit-test/tests/profiler/enterjit-osr-disabling-earlyret.js new file mode 100644 index 0000000000..f8184696e8 --- /dev/null +++ b/js/src/jit-test/tests/profiler/enterjit-osr-disabling-earlyret.js @@ -0,0 +1,13 @@ +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 20); + +enableGeckoProfilingWithSlowAssertions(); +(function() { + var n = 50; + while (n--) { + disableGeckoProfiling(); + if (!n) + return; + enableGeckoProfilingWithSlowAssertions(); + } +})(); diff --git a/js/src/jit-test/tests/profiler/enterjit-osr-disabling.js b/js/src/jit-test/tests/profiler/enterjit-osr-disabling.js new file mode 100644 index 0000000000..e0663e6a95 --- /dev/null +++ b/js/src/jit-test/tests/profiler/enterjit-osr-disabling.js @@ -0,0 +1,9 @@ +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 20); + +enableGeckoProfilingWithSlowAssertions(); +(function() { + disableGeckoProfiling(); + var n = 50; + while (n--); +})(); diff --git a/js/src/jit-test/tests/profiler/enterjit-osr-enabling-earlyret.js b/js/src/jit-test/tests/profiler/enterjit-osr-enabling-earlyret.js new file mode 100644 index 0000000000..e63531f504 --- /dev/null +++ b/js/src/jit-test/tests/profiler/enterjit-osr-enabling-earlyret.js @@ -0,0 +1,12 @@ +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 20); + +(function() { + var n = 50; + while (n--) { + enableGeckoProfilingWithSlowAssertions(); + if (!n) + return; + disableGeckoProfiling(); + } +})(); diff --git a/js/src/jit-test/tests/profiler/enterjit-osr-enabling.js b/js/src/jit-test/tests/profiler/enterjit-osr-enabling.js new file mode 100644 index 0000000000..aaad2c32a4 --- /dev/null +++ b/js/src/jit-test/tests/profiler/enterjit-osr-enabling.js @@ -0,0 +1,8 @@ +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 20); + +(function() { + enableGeckoProfilingWithSlowAssertions(); + var n = 50; + while (n--); +})(); diff --git a/js/src/jit-test/tests/profiler/enterjit-osr.js b/js/src/jit-test/tests/profiler/enterjit-osr.js new file mode 100644 index 0000000000..b490fafc96 --- /dev/null +++ b/js/src/jit-test/tests/profiler/enterjit-osr.js @@ -0,0 +1,8 @@ +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 20); + +enableGeckoProfilingWithSlowAssertions(); +(function() { + var n = 50; + while (n--); +})(); diff --git a/js/src/jit-test/tests/profiler/exception-unwind-hook.js b/js/src/jit-test/tests/profiler/exception-unwind-hook.js new file mode 100644 index 0000000000..6b52d1615d --- /dev/null +++ b/js/src/jit-test/tests/profiler/exception-unwind-hook.js @@ -0,0 +1,22 @@ +// Test interaction between exception handler, debugger's onExceptionUnwind, +// and profiler's frame iterator. + +var g = newGlobal({newCompartment: true}); +var dbg = new Debugger(g); + +g.eval("" + function f() { + for (var i = 0; i < 120; i++) { + try { throw 1; } catch {} + } +}); + +var count = 0; +dbg.onExceptionUnwind = function() { + enableGeckoProfiling(); + readGeckoProfilingStack(); + disableGeckoProfiling(); + count++; +}; + +g.f(); +assertEq(count, 120); diff --git a/js/src/jit-test/tests/profiler/getter-setter-ic.js b/js/src/jit-test/tests/profiler/getter-setter-ic.js new file mode 100644 index 0000000000..225d69895e --- /dev/null +++ b/js/src/jit-test/tests/profiler/getter-setter-ic.js @@ -0,0 +1,32 @@ +// Ensure readGeckoProfilingStack() doesn't crash with Ion +// getter/setter ICs on the stack. +function getObjects() { + var objs = []; + objs.push({x: 0, get prop() { + readGeckoProfilingStack(); + return ++this.x; + }, set prop(v) { + readGeckoProfilingStack(); + this.x = v + 2; + }}); + objs.push({x: 0, y: 0, get prop() { + readGeckoProfilingStack(); + return this.y; + }, set prop(v) { + readGeckoProfilingStack(); + this.y = v; + }}); + return objs; +} +function f() { + var objs = getObjects(); + var res = 0; + for (var i=0; i<100; i++) { + var o = objs[i % objs.length]; + res += o.prop; + o.prop = i; + } + assertEq(res, 4901); +} +enableGeckoProfiling(); +f(); diff --git a/js/src/jit-test/tests/profiler/ion-rectifier-frame-bug1530351.js b/js/src/jit-test/tests/profiler/ion-rectifier-frame-bug1530351.js new file mode 100644 index 0000000000..651b635179 --- /dev/null +++ b/js/src/jit-test/tests/profiler/ion-rectifier-frame-bug1530351.js @@ -0,0 +1,6 @@ +function test(str) { + for (let i = 0; i < 100; ++i) + Reflect.apply(String.prototype.substring, str, []) +} +enableGeckoProfilingWithSlowAssertions(); +test(""); diff --git a/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-1.js b/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-1.js new file mode 100644 index 0000000000..f8e2f1f3d7 --- /dev/null +++ b/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-1.js @@ -0,0 +1,10 @@ +// |jit-test| error:ReferenceError + +enableGeckoProfiling("Math.round", "-0.49", -0); +function* g(n) { + for (var i = 0; i < n; i++) yield i; +} +var inner = g(20); +for (let target of inner) { + if (GeneratorObjectPrototype() == i(true, true) == (this) == (this)) {} +} diff --git a/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-2.js b/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-2.js new file mode 100644 index 0000000000..98e1ee0817 --- /dev/null +++ b/js/src/jit-test/tests/profiler/jsop-resume-return-bug1451385-2.js @@ -0,0 +1,9 @@ +// |jit-test| error:ReferenceError + +function* g(n) { + for (var i = 0; i < n; i++) yield i; +} +var inner = g(20); +for (let target of inner) { + if (GeneratorObjectPrototype() == i(true, true) == (this) == (this)) {} +} diff --git a/js/src/jit-test/tests/profiler/pc-count-profiler.js b/js/src/jit-test/tests/profiler/pc-count-profiler.js new file mode 100644 index 0000000000..235055af3b --- /dev/null +++ b/js/src/jit-test/tests/profiler/pc-count-profiler.js @@ -0,0 +1,53 @@ +// Smoke test for the PC count profiler. + +function f(x) { + if (x < 10) + return 0; + if (x < 50) + return 1; + return 2; +} + +L: try { + // Profile the test function "f". + pccount.start(); + for (var i = 0; i < 100; ++i) f(i); + pccount.stop(); + + // Count the profiled scripts, stop if no scripts were profiled. + var n = pccount.count(); + if (n === 0) + break L; + + // Find the script index for "f". + var scriptIndex = -1; + for (var i = 0; i < n; ++i) { + var summary = JSON.parse(pccount.summary(i)); + if (summary.name === "f") + scriptIndex = i; + } + + // Stop if the script index for "f" wasn't found. + if (scriptIndex < 0) + break L; + + // Retrieve the profiler data for "f". + var contents = pccount.contents(scriptIndex); + assertEq(typeof contents, "string"); + + // The profiler data should be parsable as JSON text. + var contents = JSON.parse(contents, (name, value) => { + // Split the Ion code segments into multiple entries. + if (name === "code") + return value.split("\n"); + + return value; + }); + + // Pretty print the profiler data. + var pretty = JSON.stringify(contents, null, 1); + print(pretty); +} finally { + // Clean-up. + pccount.purge(); +} diff --git a/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js b/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js new file mode 100644 index 0000000000..fafa9a12d9 --- /dev/null +++ b/js/src/jit-test/tests/profiler/test-baseline-eval-frame-profiling.js @@ -0,0 +1,11 @@ + +setJitCompilerOption("baseline.warmup.trigger", 10); + +function main() { + for (var i = 0; i < 50; i++) + eval("for (var j = 0; j < 50; j++) readGeckoProfilingStack();"); +} + +gczeal(2, 10000); +enableGeckoProfilingWithSlowAssertions(); +main(); diff --git a/js/src/jit-test/tests/profiler/test-bug1026485.js b/js/src/jit-test/tests/profiler/test-bug1026485.js new file mode 100644 index 0000000000..4d0acc9c4c --- /dev/null +++ b/js/src/jit-test/tests/profiler/test-bug1026485.js @@ -0,0 +1,15 @@ + +function TestCase(n, d, e, a) { + return TestCase.prototype.dump = function () {}; +} +enableGeckoProfiling(); +new TestCase(typeof Number(new Number())); +new TestCase(typeof Number(new Number(Number.NaN))); +test(); +function test() { + try { + test(); + } catch (e) { + new TestCase(); + } +} |