summaryrefslogtreecommitdiffstats
path: root/js/src/gdb/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/gdb/tests
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/gdb/tests')
-rw-r--r--js/src/gdb/tests/enum-printers.cpp25
-rw-r--r--js/src/gdb/tests/enum-printers.py47
-rw-r--r--js/src/gdb/tests/test-ExecutableAllocator.cpp49
-rw-r--r--js/src/gdb/tests/test-ExecutableAllocator.py22
-rw-r--r--js/src/gdb/tests/test-GCCellPtr.cpp62
-rw-r--r--js/src/gdb/tests/test-GCCellPtr.py17
-rw-r--r--js/src/gdb/tests/test-Interpreter.cpp88
-rw-r--r--js/src/gdb/tests/test-Interpreter.py23
-rw-r--r--js/src/gdb/tests/test-JSObject-null.py8
-rw-r--r--js/src/gdb/tests/test-JSObject.cpp54
-rw-r--r--js/src/gdb/tests/test-JSObject.py24
-rw-r--r--js/src/gdb/tests/test-JSString-null.py8
-rw-r--r--js/src/gdb/tests/test-JSString-subclasses.py7
-rw-r--r--js/src/gdb/tests/test-JSString.cpp65
-rw-r--r--js/src/gdb/tests/test-JSString.py24
-rw-r--r--js/src/gdb/tests/test-JSSymbol.cpp22
-rw-r--r--js/src/gdb/tests/test-JSSymbol.py11
-rw-r--r--js/src/gdb/tests/test-Root-null.py21
-rw-r--r--js/src/gdb/tests/test-Root.cpp67
-rw-r--r--js/src/gdb/tests/test-Root.py30
-rw-r--r--js/src/gdb/tests/test-asmjs.cpp44
-rw-r--r--js/src/gdb/tests/test-asmjs.py16
-rw-r--r--js/src/gdb/tests/test-jsbytecode.cpp32
-rw-r--r--js/src/gdb/tests/test-jsbytecode.py9
-rw-r--r--js/src/gdb/tests/test-jsid.cpp44
-rw-r--r--js/src/gdb/tests/test-jsid.py19
-rw-r--r--js/src/gdb/tests/test-jsop.cpp13
-rw-r--r--js/src/gdb/tests/test-jsop.py9
-rw-r--r--js/src/gdb/tests/test-jsval.cpp49
-rw-r--r--js/src/gdb/tests/test-jsval.py23
-rw-r--r--js/src/gdb/tests/test-prettyprinters.cpp38
-rw-r--r--js/src/gdb/tests/test-prettyprinters.py40
-rw-r--r--js/src/gdb/tests/test-unwind.cpp76
-rw-r--r--js/src/gdb/tests/test-unwind.py61
-rw-r--r--js/src/gdb/tests/typedef-printers.cpp11
-rw-r--r--js/src/gdb/tests/typedef-printers.py18
36 files changed, 1176 insertions, 0 deletions
diff --git a/js/src/gdb/tests/enum-printers.cpp b/js/src/gdb/tests/enum-printers.cpp
new file mode 100644
index 0000000000..dfb1cd7b46
--- /dev/null
+++ b/js/src/gdb/tests/enum-printers.cpp
@@ -0,0 +1,25 @@
+#include "gdb-tests.h"
+
+#include <stdint.h>
+
+enum unscoped_no_storage { EnumValue1 };
+
+enum unscoped_with_storage : uint8_t { EnumValue2 };
+
+enum class scoped_no_storage { EnumValue3 };
+
+enum class scoped_with_storage : uint8_t { EnumValue4 };
+
+FRAGMENT(enum_printers, one) {
+ unscoped_no_storage i1 = EnumValue1;
+ unscoped_with_storage i2 = EnumValue2;
+ scoped_no_storage i3 = scoped_no_storage::EnumValue3;
+ scoped_with_storage i4 = scoped_with_storage::EnumValue4;
+
+ breakpoint();
+
+ use(i1);
+ use(i2);
+ use(i3);
+ use(i4);
+}
diff --git a/js/src/gdb/tests/enum-printers.py b/js/src/gdb/tests/enum-printers.py
new file mode 100644
index 0000000000..ff2c088dc8
--- /dev/null
+++ b/js/src/gdb/tests/enum-printers.py
@@ -0,0 +1,47 @@
+# Test that we can find pretty-printers for enums.
+# flake8: noqa: F821
+
+import mozilla.prettyprinters
+
+
+@mozilla.prettyprinters.pretty_printer("unscoped_no_storage")
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+
+ def to_string(self):
+ return "unscoped_no_storage::success"
+
+
+@mozilla.prettyprinters.pretty_printer("unscoped_with_storage")
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+
+ def to_string(self):
+ return "unscoped_with_storage::success"
+
+
+@mozilla.prettyprinters.pretty_printer("scoped_no_storage")
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+
+ def to_string(self):
+ return "scoped_no_storage::success"
+
+
+@mozilla.prettyprinters.pretty_printer("scoped_with_storage")
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+
+ def to_string(self):
+ return "scoped_with_storage::success"
+
+
+run_fragment("enum_printers.one")
+assert_pretty("i1", "unscoped_no_storage::success")
+assert_pretty("i2", "unscoped_with_storage::success")
+assert_pretty("i3", "scoped_no_storage::success")
+assert_pretty("i4", "scoped_with_storage::success")
diff --git a/js/src/gdb/tests/test-ExecutableAllocator.cpp b/js/src/gdb/tests/test-ExecutableAllocator.cpp
new file mode 100644
index 0000000000..2a2ae1cf63
--- /dev/null
+++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp
@@ -0,0 +1,49 @@
+#include "gdb-tests.h"
+
+#include "jit/ExecutableAllocator.h"
+#include "vm/JSContext.h"
+
+FRAGMENT(ExecutableAllocator, empty) {
+ using namespace js::jit;
+ ExecutableAllocator execAlloc;
+
+ breakpoint();
+
+ use(execAlloc);
+}
+
+FRAGMENT(ExecutableAllocator, onepool) {
+ using namespace js::jit;
+ ExecutablePool* pool = nullptr;
+ ExecutableAllocator execAlloc;
+ execAlloc.alloc(cx, 16 * 1024, &pool, CodeKind::Baseline);
+
+ breakpoint();
+
+ use(pool);
+ use(execAlloc);
+}
+
+FRAGMENT(ExecutableAllocator, twopools) {
+ using namespace js::jit;
+ const size_t INIT_ALLOC_SIZE = 16 * 1024;
+ const size_t ALLOC_SIZE = 32 * 1024;
+ ExecutablePool* init = nullptr;
+ ExecutablePool* pool = nullptr;
+ ExecutableAllocator execAlloc;
+ size_t allocated = 0;
+
+ execAlloc.alloc(cx, INIT_ALLOC_SIZE, &init, CodeKind::Baseline);
+
+ do { // Keep allocating until we get a second pool.
+ execAlloc.alloc(cx, ALLOC_SIZE, &pool, CodeKind::Ion);
+ allocated += ALLOC_SIZE;
+ } while (pool == init);
+
+ breakpoint();
+
+ use(execAlloc);
+ init->release(INIT_ALLOC_SIZE, CodeKind::Baseline);
+ init->release(allocated - ALLOC_SIZE, CodeKind::Ion);
+ pool->release(ALLOC_SIZE, CodeKind::Ion);
+}
diff --git a/js/src/gdb/tests/test-ExecutableAllocator.py b/js/src/gdb/tests/test-ExecutableAllocator.py
new file mode 100644
index 0000000000..bec2dda623
--- /dev/null
+++ b/js/src/gdb/tests/test-ExecutableAllocator.py
@@ -0,0 +1,22 @@
+# Tests for ExecutableAllocator pretty-printing
+# Ignore flake8 errors "undefined name 'assert_regexp_pretty'"
+# As it caused by the way we instanciate this file
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "JS::GCCellPtr")
+
+run_fragment("ExecutableAllocator.empty")
+
+assert_pretty("execAlloc", "ExecutableAllocator([])")
+
+run_fragment("ExecutableAllocator.onepool")
+
+reExecPool = "ExecutablePool [a-f0-9]{8,}-[a-f0-9]{8,}"
+assert_regexp_pretty("pool", reExecPool)
+assert_regexp_pretty("execAlloc", "ExecutableAllocator\(\[" + reExecPool + "\]\)")
+
+run_fragment("ExecutableAllocator.twopools")
+
+assert_regexp_pretty(
+ "execAlloc", "ExecutableAllocator\(\[" + reExecPool + ", " + reExecPool + "\]\)"
+)
diff --git a/js/src/gdb/tests/test-GCCellPtr.cpp b/js/src/gdb/tests/test-GCCellPtr.cpp
new file mode 100644
index 0000000000..8dcf135be8
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.cpp
@@ -0,0 +1,62 @@
+#include "gdb-tests.h"
+
+#include "js/CompileOptions.h"
+#include "js/CompilationAndEvaluation.h"
+#include "js/GlobalObject.h"
+#include "js/HeapAPI.h"
+#include "js/RegExpFlags.h"
+#include "js/SourceText.h"
+#include "js/Symbol.h"
+#include "js/TypeDecls.h"
+#include "vm/BigIntType.h"
+#include "vm/JSObject.h"
+#include "vm/RegExpObject.h"
+#include "vm/Shape.h"
+
+#include "vm/JSObject-inl.h"
+
+FRAGMENT(GCCellPtr, simple) {
+ JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+ JS::Rooted<JS::Symbol*> unique(cx, JS::NewSymbol(cx, nullptr));
+ JS::Rooted<JS::BigInt*> zeroBigInt(cx, JS::BigInt::zero(cx));
+ JS::Rooted<js::RegExpObject*> regExp(
+ cx, js::RegExpObject::create(cx, u"", 0, JS::RegExpFlags{},
+ js::GenericObject));
+ JS::Rooted<js::RegExpShared*> rootedRegExpShared(
+ cx, js::RegExpObject::getShared(cx, regExp));
+
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(__FILE__, __LINE__);
+ JS::SourceText<char16_t> srcBuf;
+ (void)srcBuf.init(cx, nullptr, 0, JS::SourceOwnership::Borrowed);
+ JS::RootedScript emptyScript(cx, JS::Compile(cx, options, srcBuf));
+
+ // Inline TraceKinds.
+ JS::GCCellPtr nulll(nullptr);
+ JS::GCCellPtr object(glob.get());
+ JS::GCCellPtr string(empty.get());
+ JS::GCCellPtr symbol(unique.get());
+ JS::GCCellPtr bigint(zeroBigInt.get());
+ JS::GCCellPtr shape(glob->shape());
+
+ // Out-of-line TraceKinds.
+ JS::GCCellPtr baseShape(glob->shape()->base());
+ // JitCode can't easily be tested here, so skip it.
+ JS::GCCellPtr script(emptyScript.get());
+ JS::GCCellPtr scope(emptyScript->bodyScope());
+ JS::GCCellPtr regExpShared(rootedRegExpShared.get());
+
+ breakpoint();
+
+ use(nulll);
+ use(object);
+ use(string);
+ use(symbol);
+ use(bigint);
+ use(shape);
+ use(baseShape);
+ use(script);
+ use(scope);
+ use(regExpShared);
+}
diff --git a/js/src/gdb/tests/test-GCCellPtr.py b/js/src/gdb/tests/test-GCCellPtr.py
new file mode 100644
index 0000000000..355ca69806
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.py
@@ -0,0 +1,17 @@
+# Tests for GCCellPtr pretty-printing
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "JS::GCCellPtr")
+
+run_fragment("GCCellPtr.simple")
+
+assert_pretty("nulll", "JS::GCCellPtr(nullptr)")
+assert_pretty("object", "JS::GCCellPtr((JSObject*) )")
+assert_pretty("string", "JS::GCCellPtr((JSString*) )")
+assert_pretty("symbol", "JS::GCCellPtr((JS::Symbol*) )")
+assert_pretty("bigint", "JS::GCCellPtr((JS::BigInt*) )")
+assert_pretty("shape", "JS::GCCellPtr((js::Shape*) )")
+assert_pretty("baseShape", "JS::GCCellPtr((js::BaseShape*) )")
+assert_pretty("script", "JS::GCCellPtr((js::BaseScript*) )")
+assert_pretty("scope", "JS::GCCellPtr((js::Scope*) )")
+assert_pretty("regExpShared", "JS::GCCellPtr((js::RegExpShared*) )")
diff --git a/js/src/gdb/tests/test-Interpreter.cpp b/js/src/gdb/tests/test-Interpreter.cpp
new file mode 100644
index 0000000000..cc844d59a0
--- /dev/null
+++ b/js/src/gdb/tests/test-Interpreter.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 "gdb-tests.h"
+
+#include "vm/Stack.h"
+
+namespace js {
+
+void GDBTestInitInterpreterRegs(InterpreterRegs& regs,
+ js::InterpreterFrame* fp_, JS::Value* sp,
+ uint8_t* pc) {
+ regs.fp_ = fp_;
+ regs.sp = sp;
+ regs.pc = pc;
+}
+
+void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame,
+ InterpreterFrame* ptr) {
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_InterpreterFrame;
+}
+
+void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame,
+ jit::BaselineFrame* ptr) {
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_BaselineFrame;
+}
+
+void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame,
+ jit::RematerializedFrame* ptr) {
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame;
+}
+
+void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame,
+ wasm::DebugFrame* ptr) {
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_WasmDebugFrame;
+}
+
+} // namespace js
+
+FRAGMENT(Interpreter, Regs) {
+ struct FakeFrame {
+ js::InterpreterFrame frame;
+ JS::Value slot0;
+ JS::Value slot1;
+ JS::Value slot2;
+ } fakeFrame;
+ uint8_t fakeOpcode = uint8_t(JSOp::True);
+
+ js::InterpreterRegs regs;
+ js::GDBTestInitInterpreterRegs(regs, &fakeFrame.frame, &fakeFrame.slot2,
+ &fakeOpcode);
+
+ breakpoint();
+
+ use(regs);
+}
+
+FRAGMENT(Interpreter, AbstractFramePtr) {
+ js::AbstractFramePtr ifptr;
+ GDBTestInitAbstractFramePtr(ifptr,
+ (js::InterpreterFrame*)uintptr_t(0x8badf00));
+
+ js::AbstractFramePtr bfptr;
+ GDBTestInitAbstractFramePtr(bfptr,
+ (js::jit::BaselineFrame*)uintptr_t(0xbadcafe0));
+
+ js::AbstractFramePtr rfptr;
+ GDBTestInitAbstractFramePtr(
+ rfptr, (js::jit::RematerializedFrame*)uintptr_t(0xdabbad00));
+
+ js::AbstractFramePtr sfptr;
+ GDBTestInitAbstractFramePtr(sfptr,
+ (js::wasm::DebugFrame*)uintptr_t(0xcb98ad00));
+
+ breakpoint();
+
+ use(ifptr);
+ use(bfptr);
+ use(rfptr);
+ use(sfptr);
+}
diff --git a/js/src/gdb/tests/test-Interpreter.py b/js/src/gdb/tests/test-Interpreter.py
new file mode 100644
index 0000000000..7a0279dc4d
--- /dev/null
+++ b/js/src/gdb/tests/test-Interpreter.py
@@ -0,0 +1,23 @@
+# Test printing interpreter internal data structures.
+# Ignore flake8 errors "undefined name 'assert_pretty'"
+# As it caused by the way we instanciate this file
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "js::InterpreterRegs")
+
+run_fragment("Interpreter.Regs")
+
+assert_pretty("regs", "{ fp_ = , sp = fp_.slots() + 2, pc = (JSOp::True) }")
+
+run_fragment("Interpreter.AbstractFramePtr")
+
+assert_pretty(
+ "ifptr", "AbstractFramePtr ((js::InterpreterFrame *) ) = {ptr_ = 146464513}"
+)
+assert_pretty(
+ "bfptr", "AbstractFramePtr ((js::jit::BaselineFrame *) ) = {ptr_ = 3135025122}"
+)
+assert_pretty(
+ "rfptr",
+ "AbstractFramePtr ((js::jit::RematerializedFrame *) ) = {ptr_ = 3669732611}",
+)
diff --git a/js/src/gdb/tests/test-JSObject-null.py b/js/src/gdb/tests/test-JSObject-null.py
new file mode 100644
index 0000000000..fcda681aea
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject-null.py
@@ -0,0 +1,8 @@
+# flake8: noqa: F821
+
+gdb.execute("set print address on")
+
+run_fragment("JSObject.null")
+
+assert_pretty("null", "0x0")
+assert_pretty("nullRaw", "0x0")
diff --git a/js/src/gdb/tests/test-JSObject.cpp b/js/src/gdb/tests/test-JSObject.cpp
new file mode 100644
index 0000000000..b1780a7332
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject.cpp
@@ -0,0 +1,54 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+#include "js/GlobalObject.h"
+#include "js/Object.h" // JS::GetClass
+
+FRAGMENT(JSObject, simple) {
+ JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSObject*> plain(cx, JS_NewPlainObject(cx));
+ JS::Rooted<JSObject*> objectProto(cx, JS::GetRealmObjectPrototype(cx));
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JSObject*> func(
+ cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "dys"));
+ JS::Rooted<JSObject*> anon(
+ cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, nullptr));
+ JS::Rooted<JSFunction*> funcPtr(
+ cx, JS_NewFunction(cx, (JSNative)1, 0, 0, "formFollows"));
+
+ // JS_NewObject will now assert if you feed it a bad class name, so mangle
+ // the name after construction.
+ char namebuf[20] = "goodname";
+ static JSClass cls{namebuf};
+ JS::RootedObject badClassName(cx, JS_NewObject(cx, &cls));
+ MOZ_RELEASE_ASSERT(badClassName);
+ strcpy(namebuf, "\xc7X");
+
+ JSObject& plainRef = *plain;
+ JSFunction& funcRef = *funcPtr;
+ JSObject* plainRaw = plain;
+ JSObject* funcRaw = func;
+
+ breakpoint();
+
+ use(glob);
+ use(plain);
+ use(objectProto);
+ use(func);
+ use(anon);
+ use(funcPtr);
+ use(&plainRef);
+ use(&funcRef);
+ use(JS::GetClass((JSObject*)&funcRef));
+ use(plainRaw);
+ use(funcRaw);
+}
+
+FRAGMENT(JSObject, null) {
+ JS::Rooted<JSObject*> null(cx, nullptr);
+ JSObject* nullRaw = null;
+
+ breakpoint();
+
+ use(null);
+ use(nullRaw);
+}
diff --git a/js/src/gdb/tests/test-JSObject.py b/js/src/gdb/tests/test-JSObject.py
new file mode 100644
index 0000000000..6f52317d28
--- /dev/null
+++ b/js/src/gdb/tests/test-JSObject.py
@@ -0,0 +1,24 @@
+# Printing JSObjects.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "ptr-to-JSObject")
+assert_subprinter_registered("SpiderMonkey", "ref-to-JSObject")
+
+run_fragment("JSObject.simple")
+
+# These patterns look a little strange because of prologue.py's 'set print
+# address off', which avoids putting varying addresses in the output. After
+# the '(JSObject *) ', there is a 'void *' value printing as the empty
+# string.
+
+assert_pretty("glob", "(JSObject *) [object global]")
+assert_pretty("plain", "(JSObject *) [object Object]")
+assert_pretty("objectProto", "(JSObject *) [object Object] used_as_prototype")
+assert_pretty("func", '(JSObject *) [object Function "dys"]')
+assert_pretty("anon", "(JSObject *) [object Function <unnamed>]")
+assert_pretty("funcPtr", '(JSFunction *) [object Function "formFollows"]')
+
+assert_pretty("badClassName", "(JSObject *) [object \\307X]")
+
+assert_pretty("plainRef", "(JSObject &) @ [object Object]")
+assert_pretty("funcRef", '(JSFunction &) @ [object Function "formFollows"]')
diff --git a/js/src/gdb/tests/test-JSString-null.py b/js/src/gdb/tests/test-JSString-null.py
new file mode 100644
index 0000000000..26bbfcf428
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString-null.py
@@ -0,0 +1,8 @@
+# flake8: noqa: F821
+
+gdb.execute("set print address on")
+
+run_fragment("JSString.null")
+
+assert_pretty("null", "0x0")
+assert_pretty("nullRaw", "0x0")
diff --git a/js/src/gdb/tests/test-JSString-subclasses.py b/js/src/gdb/tests/test-JSString-subclasses.py
new file mode 100644
index 0000000000..b685f46aff
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString-subclasses.py
@@ -0,0 +1,7 @@
+# flake8: noqa: F821
+
+# We can print pointers to subclasses of JSString.
+
+run_fragment("JSString.subclasses")
+
+assert_pretty("linear", '"Hi!"')
diff --git a/js/src/gdb/tests/test-JSString.cpp b/js/src/gdb/tests/test-JSString.cpp
new file mode 100644
index 0000000000..9e930814da
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString.cpp
@@ -0,0 +1,65 @@
+#include "gdb-tests.h"
+
+#include "vm/JSContext.h"
+// When JSGC_ANALYSIS is #defined, Rooted<JSLinearString*> needs the definition
+// of JSLinearString in order to figure out its ThingRootKind
+#include "vm/StringType.h"
+
+FRAGMENT(JSString, simple) {
+ JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+ JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1));
+ JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z"));
+
+ // I expect this will be a non-inlined string.
+ JS::Rooted<JSString*> stars(cx,
+ JS_NewStringCopyZ(cx,
+ "*************************"
+ "*************************"
+ "*************************"
+ "*************************"));
+
+ // This may well be an inlined string.
+ JS::Rooted<JSString*> xz(cx, JS_ConcatStrings(cx, x, z));
+
+ // This will probably be a rope.
+ JS::Rooted<JSString*> doubleStars(cx, JS_ConcatStrings(cx, stars, stars));
+
+ // Ensure we're not confused by typedefs for pointer types.
+ JSString* xRaw = x;
+
+ breakpoint();
+
+ use(empty);
+ use(x);
+ use(z);
+ use(stars);
+ use(xz);
+ use(doubleStars);
+ use(xRaw);
+}
+
+FRAGMENT(JSString, null) {
+ JS::Rooted<JSString*> null(cx, nullptr);
+ JSString* nullRaw = null;
+
+ breakpoint();
+
+ use(null);
+ use(nullRaw);
+}
+
+FRAGMENT(JSString, subclasses) {
+ JS::Rooted<JSLinearString*> linear(
+ cx, JS_EnsureLinearString(cx, JS_NewStringCopyZ(cx, "Hi!")));
+
+ breakpoint();
+
+ use(linear);
+}
+
+FRAGMENT(JSString, atom) {
+ JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10);
+ breakpoint();
+
+ use(molybdenum);
+}
diff --git a/js/src/gdb/tests/test-JSString.py b/js/src/gdb/tests/test-JSString.py
new file mode 100644
index 0000000000..99cebc4ab5
--- /dev/null
+++ b/js/src/gdb/tests/test-JSString.py
@@ -0,0 +1,24 @@
+# Printing JSStrings.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "ptr-to-JSString")
+run_fragment("JSString.simple")
+
+assert_pretty("empty", '""')
+assert_pretty("x", '"x"')
+assert_pretty("z", '"z"')
+assert_pretty("xz", '"xz"')
+
+stars = gdb.parse_and_eval("stars")
+assert_eq(str(stars), "'*' <repeats 100 times>")
+
+doubleStars = gdb.parse_and_eval("doubleStars")
+assert_eq(str(doubleStars), "'*' <repeats 200 times>")
+
+assert_pretty("xRaw", '"x"')
+
+# JSAtom *
+
+run_fragment("JSString.atom")
+
+assert_pretty("molybdenum", '"molybdenum"')
diff --git a/js/src/gdb/tests/test-JSSymbol.cpp b/js/src/gdb/tests/test-JSSymbol.cpp
new file mode 100644
index 0000000000..815f2d9e29
--- /dev/null
+++ b/js/src/gdb/tests/test-JSSymbol.cpp
@@ -0,0 +1,22 @@
+#include "gdb-tests.h"
+
+#include "js/String.h"
+#include "js/Symbol.h"
+
+FRAGMENT(JSSymbol, simple) {
+ using namespace JS;
+
+ RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
+
+ Rooted<Symbol*> unique(cx, NewSymbol(cx, nullptr));
+ Rooted<Symbol*> unique_with_desc(cx, NewSymbol(cx, hello));
+ Rooted<Symbol*> registry(cx, GetSymbolFor(cx, hello));
+ Rooted<Symbol*> well_known(cx, GetWellKnownSymbol(cx, SymbolCode::iterator));
+
+ breakpoint();
+
+ use(unique);
+ use(unique_with_desc);
+ use(registry);
+ use(well_known);
+}
diff --git a/js/src/gdb/tests/test-JSSymbol.py b/js/src/gdb/tests/test-JSSymbol.py
new file mode 100644
index 0000000000..a0dba5c036
--- /dev/null
+++ b/js/src/gdb/tests/test-JSSymbol.py
@@ -0,0 +1,11 @@
+# Printing JS::Symbols.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "ptr-to-JS::Symbol")
+
+run_fragment("JSSymbol.simple")
+
+assert_pretty("unique", "Symbol()")
+assert_pretty("unique_with_desc", 'Symbol("Hello!")')
+assert_pretty("registry", 'Symbol.for("Hello!")')
+assert_pretty("well_known", "Symbol.iterator")
diff --git a/js/src/gdb/tests/test-Root-null.py b/js/src/gdb/tests/test-Root-null.py
new file mode 100644
index 0000000000..839fa069c5
--- /dev/null
+++ b/js/src/gdb/tests/test-Root-null.py
@@ -0,0 +1,21 @@
+# Test printing roots that refer to NULL pointers.
+
+# Since mozilla.prettyprinters.Pointer declines to create pretty-printers
+# for null pointers, GDB built-in printing code ends up handling them. But
+# as of 2012-11, GDB suppresses printing pointers in replacement values:
+# see: http://sourceware.org/ml/gdb/2012-11/msg00055.html
+#
+# Thus, if the pretty-printer for JS::Rooted simply returns the referent as
+# a replacement value (which seems reasonable enough, if you want the
+# pretty-printer to be completely transparent), and the referent is a null
+# pointer, it prints as nothing at all.
+#
+# This test ensures that the JS::Rooted pretty-printer doesn't make that
+# mistake.
+# flake8: noqa: F821
+
+gdb.execute("set print address on")
+
+run_fragment("Root.null")
+
+assert_pretty("null", "0x0")
diff --git a/js/src/gdb/tests/test-Root.cpp b/js/src/gdb/tests/test-Root.cpp
new file mode 100644
index 0000000000..f93beba88a
--- /dev/null
+++ b/js/src/gdb/tests/test-Root.cpp
@@ -0,0 +1,67 @@
+#include "gdb-tests.h"
+
+#include "jsapi.h"
+
+#include "gc/Barrier.h"
+#include "js/Array.h" // JS::NewArrayObject
+#include "js/GlobalObject.h"
+#include "js/ValueArray.h"
+#include "vm/JSFunction.h"
+
+using namespace js;
+
+FRAGMENT(Root, null) {
+ JS::Rooted<JSObject*> null(cx, nullptr);
+
+ breakpoint();
+
+ use(null);
+}
+
+void callee(JS::Handle<JSObject*> obj,
+ JS::MutableHandle<JSObject*> mutableObj) {
+ // Prevent the linker from unifying this function with others that are
+ // equivalent in machine code but not type.
+ fprintf(stderr, "Called " __FILE__ ":callee\n");
+ breakpoint();
+}
+
+FRAGMENT(Root, handle) {
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ callee(global, &global);
+ use(global);
+}
+
+FRAGMENT(Root, HeapSlot) {
+ JS::Rooted<JS::Value> plinth(
+ cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth")));
+ JS::Rooted<JSObject*> array(
+ cx, JS::NewArrayObject(cx, JS::HandleValueArray(plinth)));
+
+ breakpoint();
+
+ use(plinth);
+ use(array);
+}
+
+FRAGMENT(Root, barriers) {
+ JSObject* obj = JS_NewPlainObject(cx);
+ PreBarriered<JSObject*> prebarriered(obj);
+ GCPtr<JSObject*> heapptr(obj);
+ HeapPtr<JSObject*> relocatable(obj);
+
+ JS::Value val = JS::ObjectValue(*obj);
+ PreBarriered<JS::Value> prebarrieredValue(JS::ObjectValue(*obj));
+ GCPtr<JS::Value> heapValue(JS::ObjectValue(*obj));
+ HeapPtr<JS::Value> relocatableValue(JS::ObjectValue(*obj));
+
+ breakpoint();
+
+ use(prebarriered);
+ use(heapptr);
+ use(relocatable);
+ use(val);
+ use(prebarrieredValue);
+ use(heapValue);
+ use(relocatableValue);
+}
diff --git a/js/src/gdb/tests/test-Root.py b/js/src/gdb/tests/test-Root.py
new file mode 100644
index 0000000000..e593a972ab
--- /dev/null
+++ b/js/src/gdb/tests/test-Root.py
@@ -0,0 +1,30 @@
+# Test printing Handles.
+# Ignore flake8 errors "undefined name 'assert_pretty'"
+# As it caused by the way we instanciate this file
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "instantiations-of-JS::Rooted")
+assert_subprinter_registered("SpiderMonkey", "instantiations-of-JS::Handle")
+assert_subprinter_registered("SpiderMonkey", "instantiations-of-JS::MutableHandle")
+assert_subprinter_registered("SpiderMonkey", "instantiations-of-js::BarrieredBase")
+
+run_fragment("Root.handle")
+
+assert_pretty("obj", "(JSObject * const) [object global]")
+assert_pretty("mutableObj", "(JSObject *) [object global]")
+
+run_fragment("Root.HeapSlot")
+
+# This depends on implementation details of arrays, but since HeapSlot is
+# not a public type, I'm not sure how to avoid doing *something* ugly.
+assert_pretty("((js::NativeObject *) array.ptr)->elements_[0]", '$JS::Value("plinth")')
+
+run_fragment("Root.barriers")
+
+assert_pretty("prebarriered", "(JSObject *) [object Object]")
+assert_pretty("heapptr", "(JSObject *) [object Object]")
+assert_pretty("relocatable", "(JSObject *) [object Object]")
+assert_pretty("val", "$JS::Value((JSObject *) [object Object])")
+assert_pretty("heapValue", "$JS::Value((JSObject *) [object Object])")
+assert_pretty("prebarrieredValue", "$JS::Value((JSObject *) [object Object])")
+assert_pretty("relocatableValue", "$JS::Value((JSObject *) [object Object])")
diff --git a/js/src/gdb/tests/test-asmjs.cpp b/js/src/gdb/tests/test-asmjs.cpp
new file mode 100644
index 0000000000..8a9fab3b73
--- /dev/null
+++ b/js/src/gdb/tests/test-asmjs.cpp
@@ -0,0 +1,44 @@
+#include "gdb-tests.h"
+#include "js/CompilationAndEvaluation.h"
+#include "js/CompileOptions.h"
+#include "js/RootingAPI.h"
+#include "js/SourceText.h"
+#include "js/Value.h"
+#include "mozilla/Utf8.h"
+#include "util/Text.h"
+
+#include <string.h>
+
+FRAGMENT(asmjs, segfault) {
+ constexpr unsigned line0 = __LINE__;
+ static const char chars[] =
+ "function f(glob, ffi, heap) {\n"
+ " \"use asm\";\n"
+ " var f32 = new glob.Float32Array(heap);\n"
+ " function g(n) {\n"
+ " n = n | 0;\n"
+ " return +f32[n>>2];\n"
+ " }\n"
+ " return g;\n"
+ "}\n"
+ "\n"
+ "var func = f(this, null, new ArrayBuffer(0x10000));\n"
+ "func(0x10000 << 2);\n"
+ "'ok'\n";
+
+ JS::CompileOptions opts(cx);
+ opts.setFileAndLine(__FILE__, line0 + 1);
+ opts.asmJSOption = JS::AsmJSOption::Enabled;
+
+ JS::SourceText<mozilla::Utf8Unit> srcBuf;
+ JS::Rooted<JS::Value> rval(cx);
+
+ bool ok =
+ srcBuf.init(cx, chars, js_strlen(chars), JS::SourceOwnership::Borrowed) &&
+ JS::Evaluate(cx, opts, srcBuf, &rval);
+
+ breakpoint();
+
+ use(ok);
+ use(rval);
+}
diff --git a/js/src/gdb/tests/test-asmjs.py b/js/src/gdb/tests/test-asmjs.py
new file mode 100644
index 0000000000..9e3ed7fc89
--- /dev/null
+++ b/js/src/gdb/tests/test-asmjs.py
@@ -0,0 +1,16 @@
+# Test for special asmjs SIGSEGV-handling.
+#
+# Expected behavior is for the asm.js code in the following fragment to trigger
+# SIGSEGV. The code in js/src/gdb/mozilla/asmjs.py should prevent GDB from
+# handling that signal.
+# flake8: noqa: F821
+
+run_fragment("asmjs.segfault")
+
+# If SIGSEGV handling is broken, GDB would have stopped at the SIGSEGV signal.
+# The breakpoint would not have hit, and run_fragment would have thrown.
+#
+# So if we get here, and the asm.js code actually ran, we win.
+
+assert_pretty("ok", "true")
+assert_pretty("rval", '$JS::Value("ok")')
diff --git a/js/src/gdb/tests/test-jsbytecode.cpp b/js/src/gdb/tests/test-jsbytecode.cpp
new file mode 100644
index 0000000000..c28c6da28a
--- /dev/null
+++ b/js/src/gdb/tests/test-jsbytecode.cpp
@@ -0,0 +1,32 @@
+#include "gdb-tests.h"
+#include "js/CompilationAndEvaluation.h"
+#include "js/CompileOptions.h"
+#include "js/SourceText.h"
+#include "util/Text.h"
+#include "vm/JSFunction.h"
+#include "vm/JSScript.h"
+#include "mozilla/Utf8.h"
+
+FRAGMENT(jsbytecode, simple) {
+ constexpr unsigned line0 = __LINE__;
+ static const char chars[] = R"(
+ debugger;
+ )";
+
+ JS::CompileOptions opts(cx);
+ opts.setFileAndLine(__FILE__, line0 + 1);
+
+ JS::SourceText<mozilla::Utf8Unit> srcBuf;
+ JS::Rooted<JS::Value> rval(cx);
+
+ bool ok =
+ srcBuf.init(cx, chars, js_strlen(chars), JS::SourceOwnership::Borrowed);
+
+ JSScript* script = JS::Compile(cx, opts, srcBuf);
+ jsbytecode* code = script->code();
+
+ breakpoint();
+
+ use(ok);
+ use(code);
+}
diff --git a/js/src/gdb/tests/test-jsbytecode.py b/js/src/gdb/tests/test-jsbytecode.py
new file mode 100644
index 0000000000..e69732d4c7
--- /dev/null
+++ b/js/src/gdb/tests/test-jsbytecode.py
@@ -0,0 +1,9 @@
+# Basic unit tests for jsbytecode* pretty-printer.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "ptr-to-jsbytecode")
+
+run_fragment("jsbytecode.simple")
+
+assert_pretty("ok", "true")
+assert_pretty("code", " (JSOp::Debugger)")
diff --git a/js/src/gdb/tests/test-jsid.cpp b/js/src/gdb/tests/test-jsid.cpp
new file mode 100644
index 0000000000..8104abcccd
--- /dev/null
+++ b/js/src/gdb/tests/test-jsid.cpp
@@ -0,0 +1,44 @@
+#include "gdb-tests.h"
+
+#include "js/String.h"
+#include "js/Symbol.h"
+
+FRAGMENT(jsid, simple) {
+ const char* chars = "moon";
+ JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, chars));
+ JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinString(cx, chars));
+ JS::Rooted<jsid> string_id(cx, JS::PropertyKey::fromPinnedString(interned));
+ JS::Rooted<jsid> int_id(cx, JS::PropertyKey::Int(1729));
+ JS::Rooted<jsid> unique_symbol_id(
+ cx, JS::PropertyKey::Symbol(JS::NewSymbol(cx, interned)));
+ JS::Rooted<jsid> registry_symbol_id(
+ cx, JS::PropertyKey::Symbol(JS::GetSymbolFor(cx, interned)));
+ JS::Rooted<jsid> well_known_symbol_id(
+ cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::iterator));
+ jsid void_id = JS::PropertyKey::Void();
+
+ breakpoint();
+
+ use(string_id);
+ use(int_id);
+ use(unique_symbol_id);
+ use(registry_symbol_id);
+ use(well_known_symbol_id);
+ use(void_id);
+}
+
+void jsid_handles(JS::Handle<jsid> jsid_handle,
+ JS::MutableHandle<jsid> mutable_jsid_handle) {
+ // Prevent the linker from unifying this function with others that are
+ // equivalent in machine code but not type.
+ fprintf(stderr, "Called " __FILE__ ":jsid_handles\n");
+ breakpoint();
+}
+
+FRAGMENT(jsid, handles) {
+ const char* chars = "shovel";
+ JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, chars));
+ JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinString(cx, chars));
+ JS::Rooted<jsid> string_id(cx, JS::PropertyKey::fromPinnedString(interned));
+ jsid_handles(string_id, &string_id);
+}
diff --git a/js/src/gdb/tests/test-jsid.py b/js/src/gdb/tests/test-jsid.py
new file mode 100644
index 0000000000..ea43439f2f
--- /dev/null
+++ b/js/src/gdb/tests/test-jsid.py
@@ -0,0 +1,19 @@
+# Tests for jsid pretty-printing
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "JS::PropertyKey")
+
+run_fragment("jsid.simple")
+
+assert_pretty("string_id", '$jsid("moon")')
+assert_pretty("int_id", "$jsid(1729)")
+unique_symbol_pretty = str(gdb.parse_and_eval("unique_symbol_id")).split("@")[0]
+assert_eq(unique_symbol_pretty, '$jsid(Symbol("moon"))')
+assert_pretty("registry_symbol_id", '$jsid(Symbol.for("moon"))')
+assert_pretty("well_known_symbol_id", "$jsid(Symbol.iterator)")
+assert_pretty("void_id", "JS::VoidPropertyKey")
+
+run_fragment("jsid.handles")
+
+assert_pretty("jsid_handle", '$jsid("shovel")')
+assert_pretty("mutable_jsid_handle", '$jsid("shovel")')
diff --git a/js/src/gdb/tests/test-jsop.cpp b/js/src/gdb/tests/test-jsop.cpp
new file mode 100644
index 0000000000..d92c8f0d2e
--- /dev/null
+++ b/js/src/gdb/tests/test-jsop.cpp
@@ -0,0 +1,13 @@
+#include "gdb-tests.h"
+
+#include "vm/BytecodeUtil.h"
+
+FRAGMENT(jsop, simple) {
+ JSOp undefined = JSOp::Undefined;
+ JSOp debugger = JSOp::Debugger;
+
+ breakpoint();
+
+ use(undefined);
+ use(debugger);
+}
diff --git a/js/src/gdb/tests/test-jsop.py b/js/src/gdb/tests/test-jsop.py
new file mode 100644
index 0000000000..fea0da6059
--- /dev/null
+++ b/js/src/gdb/tests/test-jsop.py
@@ -0,0 +1,9 @@
+# Basic unit tests for JSOp pretty-printer.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "JSOp")
+
+run_fragment("jsop.simple")
+
+assert_pretty("undefined", "JSOp::Undefined")
+assert_pretty("debugger", "JSOp::Debugger")
diff --git a/js/src/gdb/tests/test-jsval.cpp b/js/src/gdb/tests/test-jsval.cpp
new file mode 100644
index 0000000000..3a89a76a83
--- /dev/null
+++ b/js/src/gdb/tests/test-jsval.cpp
@@ -0,0 +1,49 @@
+#include "gdb-tests.h"
+
+#include "js/GlobalObject.h"
+#include "js/String.h"
+#include "js/Symbol.h"
+#include "vm/BigIntType.h"
+
+FRAGMENT(jsval, simple) {
+ using namespace JS;
+
+ RootedValue fortytwo(cx, Int32Value(42));
+ RootedValue fortytwoD(cx, DoubleValue(42));
+ RootedValue negone(cx, Int32Value(-1));
+ RootedValue undefined(cx, UndefinedValue());
+ RootedValue null(cx, NullValue());
+ RootedValue js_true(cx, BooleanValue(true));
+ RootedValue js_false(cx, BooleanValue(false));
+ RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE));
+
+ RootedValue empty_string(cx);
+ empty_string.setString(JS_NewStringCopyZ(cx, ""));
+ RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
+ RootedValue friendly_string(cx, StringValue(hello));
+ RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello)));
+ RootedValue bi(cx, BigIntValue(BigInt::zero(cx)));
+
+ RootedValue global(cx);
+ global.setObject(*CurrentGlobalOrNull(cx));
+
+ // Some interesting value that floating-point won't munge.
+ RootedValue onehundredthirtysevenonehundredtwentyeighths(
+ cx, DoubleValue(137.0 / 128.0));
+
+ breakpoint();
+
+ use(fortytwo);
+ use(fortytwoD);
+ use(negone);
+ use(undefined);
+ use(js_true);
+ use(js_false);
+ use(null);
+ use(elements_hole);
+ use(empty_string);
+ use(friendly_string);
+ use(symbol);
+ use(bi);
+ use(global);
+}
diff --git a/js/src/gdb/tests/test-jsval.py b/js/src/gdb/tests/test-jsval.py
new file mode 100644
index 0000000000..e5a42e72f4
--- /dev/null
+++ b/js/src/gdb/tests/test-jsval.py
@@ -0,0 +1,23 @@
+# Basic unit tests for jsval pretty-printer.
+# flake8: noqa: F821
+
+assert_subprinter_registered("SpiderMonkey", "JS::Value")
+
+run_fragment("jsval.simple")
+
+assert_pretty("fortytwo", "$JS::Int32Value(42)")
+assert_pretty("fortytwoD", "$JS::DoubleValue(42.0)")
+assert_pretty("negone", "$JS::Int32Value(-1)")
+assert_pretty("undefined", "$JS::UndefinedValue()")
+assert_pretty("null", "$JS::NullValue()")
+assert_pretty("js_true", "$JS::BooleanValue(true)")
+assert_pretty("js_false", "$JS::BooleanValue(false)")
+assert_pretty("elements_hole", "$JS::MagicValue(JS_ELEMENTS_HOLE)")
+assert_pretty("empty_string", '$JS::Value("")')
+assert_pretty("friendly_string", '$JS::Value("Hello!")')
+assert_pretty("symbol", '$JS::Value(Symbol.for("Hello!"))')
+assert_pretty("bi", "$JS::BigIntValue()")
+assert_pretty("global", "$JS::Value((JSObject *) [object global])")
+assert_pretty(
+ "onehundredthirtysevenonehundredtwentyeighths", "$JS::DoubleValue(1.0703125)"
+)
diff --git a/js/src/gdb/tests/test-prettyprinters.cpp b/js/src/gdb/tests/test-prettyprinters.cpp
new file mode 100644
index 0000000000..af71c52fa3
--- /dev/null
+++ b/js/src/gdb/tests/test-prettyprinters.cpp
@@ -0,0 +1,38 @@
+#include "gdb-tests.h"
+
+typedef int A;
+typedef A B;
+
+class C {};
+class D {};
+typedef C C_;
+typedef D D_;
+class E : C, D {};
+typedef E E_;
+class F : C_, D_ {};
+class G {};
+class H : F, G {};
+
+FRAGMENT(prettyprinters, implemented_types) {
+ int i = 0;
+ A a = 0;
+ B b = 0;
+ C c;
+ C_ c_;
+ E e;
+ E_ e_;
+ F f;
+ H h;
+
+ breakpoint();
+
+ use(i);
+ use(a);
+ use(b);
+ use(c);
+ use(c_);
+ use(e);
+ use(e_);
+ use(f);
+ use(h);
+}
diff --git a/js/src/gdb/tests/test-prettyprinters.py b/js/src/gdb/tests/test-prettyprinters.py
new file mode 100644
index 0000000000..f3329c0288
--- /dev/null
+++ b/js/src/gdb/tests/test-prettyprinters.py
@@ -0,0 +1,40 @@
+# Ignore flake8 errors "undefined name 'assert_pretty'"
+# As it caused by the way we instanciate this file
+# flake8: noqa: F821
+
+import mozilla.prettyprinters
+
+run_fragment("prettyprinters.implemented_types")
+
+
+def implemented_type_names(expr):
+ v = gdb.parse_and_eval(expr)
+ it = mozilla.prettyprinters.implemented_types(v.type)
+ return [str(_) for _ in it]
+
+
+assert_eq(implemented_type_names("i"), ["int"])
+assert_eq(implemented_type_names("a"), ["A", "int"])
+assert_eq(implemented_type_names("b"), ["B", "A", "int"])
+assert_eq(implemented_type_names("c"), ["C"])
+assert_eq(implemented_type_names("c_"), ["C_", "C"])
+assert_eq(implemented_type_names("e"), ["E", "C", "D"])
+assert_eq(implemented_type_names("e_"), ["E_", "E", "C", "D"])
+
+# Some compilers strip trivial typedefs in the debuginfo from classes' base
+# classes. Sometimes this can be fixed with -fno-eliminate-unused-debug-types,
+# but not always. Allow this test to pass if the typedefs are stripped.
+#
+# It would probably be better to figure out how to make the compiler emit them,
+# since I think this test is here for a reason.
+if gdb.lookup_type("F").fields()[0].name == "C_":
+ # We have the typedef info.
+ assert_eq(implemented_type_names("f"), ["F", "C_", "D_", "C", "D"])
+ assert_eq(implemented_type_names("h"), ["H", "F", "G", "C_", "D_", "C", "D"])
+else:
+ assert_eq(implemented_type_names("f"), ["F", "C", "D"])
+ assert_eq(implemented_type_names("h"), ["H", "F", "G", "C", "D"])
+
+# Check that our pretty-printers aren't interfering with printing other types.
+assert_pretty("10", "10")
+assert_pretty("(void*) 0", "") # Because of 'set print address off'
diff --git a/js/src/gdb/tests/test-unwind.cpp b/js/src/gdb/tests/test-unwind.cpp
new file mode 100644
index 0000000000..a3dc6fe89c
--- /dev/null
+++ b/js/src/gdb/tests/test-unwind.cpp
@@ -0,0 +1,76 @@
+#include "gdb-tests.h"
+#include "jsfriendapi.h" // JSFunctionSpecWithHelp
+
+#include "jit/JitOptions.h" // js::jit::JitOptions
+#include "js/CallArgs.h" // JS::CallArgs, JS::CallArgsFromVp
+#include "js/CompilationAndEvaluation.h" // JS::Evaluate
+#include "js/CompileOptions.h" // JS::CompileOptions
+#include "js/GlobalObject.h" // JS::CurrentGlobalOrNull
+#include "js/PropertyAndElement.h" // JS_DefineProperty
+#include "js/PropertyDescriptor.h" // JSPROP_ENUMERATE
+#include "js/RootingAPI.h" // JS::Rooted
+#include "js/SourceText.h" // JS::Source{Ownership,Text}
+#include "js/Value.h" // JS::Value
+
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+
+#include <stdint.h> // uint32_t
+#include <string.h> // strlen
+
+static bool Something(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ args.rval().setInt32(23);
+ breakpoint();
+ return true;
+}
+
+// clang-format off
+static const JSFunctionSpecWithHelp unwind_functions[] = {
+ JS_FN_HELP("something", Something, 0, 0,
+"something()",
+" Test function for test-unwind."),
+ JS_FS_HELP_END
+};
+// clang-format on
+
+FRAGMENT(unwind, simple) {
+ using namespace JS;
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ if (!JS_DefineFunctionsWithHelp(cx, global, unwind_functions)) {
+ return;
+ }
+
+ // Define an itercount property and use it to ensure Baseline compilation.
+ uint32_t threshold = js::jit::JitOptions.baselineJitWarmUpThreshold;
+ RootedValue val(cx, Int32Value(threshold + 10));
+ if (!JS_DefineProperty(cx, global, "itercount", val, 0)) {
+ return;
+ }
+
+ int line0 = __LINE__;
+ const char* bytes =
+ "\n"
+ "function unwindFunctionInner() {\n"
+ " for (var i = 0; i < itercount; i++) {}\n"
+ " return something();\n"
+ "}\n"
+ "\n"
+ "function unwindFunctionOuter() {\n"
+ " for (var i = 0; i < itercount; i++) {}\n"
+ " return unwindFunctionInner();\n"
+ "}\n"
+ "\n"
+ "unwindFunctionOuter();\n";
+
+ JS::CompileOptions opts(cx);
+ opts.setFileAndLine(__FILE__, line0 + 1);
+
+ JS::SourceText<mozilla::Utf8Unit> srcBuf;
+ if (!srcBuf.init(cx, bytes, strlen(bytes), JS::SourceOwnership::Borrowed)) {
+ return;
+ }
+
+ JS::Rooted<JS::Value> rval(cx);
+ JS::Evaluate(cx, opts, srcBuf, &rval);
+}
diff --git a/js/src/gdb/tests/test-unwind.py b/js/src/gdb/tests/test-unwind.py
new file mode 100644
index 0000000000..d12c4b0f6e
--- /dev/null
+++ b/js/src/gdb/tests/test-unwind.py
@@ -0,0 +1,61 @@
+# Test the unwinder and the frame filter.
+# flake8: NOQA: F821
+import platform
+
+
+def do_unwinder_test():
+ # The unwinder is disabled by default for the moment. Turn it on to check
+ # that the unwinder works as expected.
+ import gdb
+
+ gdb.execute("enable unwinder .* SpiderMonkey")
+
+ run_fragment("unwind.simple", "Something")
+
+ first = True
+ # The unwinder is a bit flaky still but should at least be able to
+ # recognize one set of entry and exit frames. This also tests to
+ # make sure we didn't end up solely in the interpreter.
+ found_entry = False
+ found_exit = False
+ found_main = False
+ found_inner = False
+ found_outer = False
+ frames = list(gdb.frames.execute_frame_filters(gdb.newest_frame(), 0, -1))
+ for frame in frames:
+ print("examining " + frame.function())
+ if first:
+ assert_eq(frame.function().startswith("Something"), True)
+ first = False
+ elif frame.function() == "<<FrameType::Exit>>":
+ found_exit = True
+ elif frame.function() == "<<FrameType::CppToJSJit>>":
+ found_entry = True
+ elif frame.function() == "main":
+ found_main = True
+ elif "unwindFunctionInner" in frame.function():
+ found_inner = True
+ elif "unwindFunctionOuter" in frame.function():
+ found_outer = True
+
+ # Had to have found a frame.
+ assert_eq(first, False)
+ # Had to have found main.
+ assert_eq(found_main, True)
+ # Had to have found the entry and exit frames.
+ assert_eq(found_exit, True)
+ assert_eq(found_entry, True)
+ # Had to have found the names of the two JS functions.
+ assert_eq(found_inner, True)
+ assert_eq(found_outer, True)
+
+
+# Only on the right platforms.
+if platform.machine() == "x86_64" and platform.system() == "Linux":
+ # Only test when gdb has the unwinder feature.
+ try:
+ import gdb.unwinder # NOQA: F401
+
+ do_unwinder_test()
+ except Exception:
+ pass
diff --git a/js/src/gdb/tests/typedef-printers.cpp b/js/src/gdb/tests/typedef-printers.cpp
new file mode 100644
index 0000000000..efb01f82cf
--- /dev/null
+++ b/js/src/gdb/tests/typedef-printers.cpp
@@ -0,0 +1,11 @@
+#include "gdb-tests.h"
+
+typedef int my_typedef;
+
+FRAGMENT(typedef_printers, one) {
+ my_typedef i = 0;
+
+ breakpoint();
+
+ use(i);
+}
diff --git a/js/src/gdb/tests/typedef-printers.py b/js/src/gdb/tests/typedef-printers.py
new file mode 100644
index 0000000000..888379ee63
--- /dev/null
+++ b/js/src/gdb/tests/typedef-printers.py
@@ -0,0 +1,18 @@
+# Test that we can find pretty-printers for typedef names, not just for
+# struct types and templates.
+# flake8: noqa: F821
+
+import mozilla.prettyprinters
+
+
+@mozilla.prettyprinters.pretty_printer("my_typedef")
+class my_typedef(object):
+ def __init__(self, value, cache):
+ pass
+
+ def to_string(self):
+ return "huzzah"
+
+
+run_fragment("typedef_printers.one")
+assert_pretty("i", "huzzah")